04 — SDK (qcobjects-sdk)¶
Purpose¶
Specify the SDK: the reusable MVC building blocks apps compose from — with the complete component/controller/effect catalogue, so the SDK README can be summarized without loss.
Source: qcobjects-sdk README (v2.5.105).
Code pins: https://github.com/QCObjects/qcobjects-sdk/blob/v2.5.105/src/ts/<module>.ts.
Definitions below are authoritative.
Scope¶
Repo QCObjects/qcobjects-sdk, npm qcobjects-sdk (v2.5.105).
Sources in src/ts/org.qcobjects.*.ts, templates in src/templates.
Install & load (normative)¶
npm install qcobjects-sdk@v2.5.105(pin per release).- Straight HTML: the SDK browser bundle (
QCObjects-SDKbundle /public/browser/index.js), not the coreQCObjects.jsCDN file. - NOTE (binding): the SDK is NOT bundled inside the core runtime — with
useSDK:true(core default) it is auto-loaded fromremoteSDKPath(https://sdk.qcobjects.dev/) in browsers, orrequire("qcobjects-sdk")fromnode_modulesin Node. Install separately only when default paths fail. - The SDK MUST depend on
qcobjectscore and MUST NOT depend onqcobjects-clior any server code.
Package namespaces (normative — ClassFactory/Import MUST use these exact names)¶
org.qcobjects.form.components:ShadowedComponent,ButtonField,InputField,TextField,EmailField,ModalEnclosureComponent,ModalComponent,SwaggerUIComponent.org.qcobjects.base.components:FormField.org.qcobjects.components.grid:GridItemComponent,GridComponent.org.qcobjects.components.list:ListItemComponent,ListComponent.org.qcobjects.components.slider:SlideListComponent,SlideItemComponent,SliderComponent.org.qcobjects.components.splashscreen:VideoSplashScreenComponent,CubeSplashScreenComponent;org.qcobjects.components.base:SplashScreenComponent.org.qcobjects.controllers,.grid,.list,.slider,.form,.swagger,org.qcobjects.modal.controllers: as named per file.org.qcobjects.modal.effects:ModalFade,ModalMoveDown,ModalMoveUp(a registered package — importable via ClassFactory).- There is NO
Package("org.qcobjects.components")— bareClassFactory/Importon that name fails.
Module export table (normative)¶
The SDK MUST export, at minimum (CJS + ESM + browser + types):
controllers, controllers.grid|slider|form|swagger, views,
components, components.grid|list|slider|splashscreen|notifications,
modal.controllers, effects, tools.canvas|layouts,
i18n_messages, models, cloud.auth.session.usertoken|data, and the
QCObjects-SDK bundle.
(Gap on record: org.qcobjects.controllers.list exists in src/ but has NO
./js/org.qcobjects.controllers.list subpath export in package.json —
ListController resolves via ClassFactory/package, not via deep import.)
Components catalogue¶
- ShadowedComponent — Shadow-DOM custom components.
<component componentClass="ShadowedComponent">or<my-custom-widget componentClass="ShadowedComponent">. Widgets register viaRegisterWidget("signup-form"). - FormField — generic form behavior with reverse data binding (no
observable overhead): put
data-field="<prop>"on inner DOM tags; setcomponentClass="FormField"on the tag; readcomponentInstance.data.executeBindings()matchesdata-fields todatafields; triggered bychange,blur,focus,keydowninside the body. - ButtonField (
<button>body) / InputField (<input>body) / TextField (<textarea>body) / EmailField (<input>body) — all FormField sub-definitions, same usage with their tag default body. - GridComponent (reserved name
"grid", inline<p>Loading...</p>template) forcescontrollerClass="DataGridController"in its constructor — pair it withDataGridController, notGridController(the CSS-only variant);rows/colsattrs; subcomponents recommended as cells. - GridItemComponent (name
"grid-item", shadowed, inline template<img src="{{image}}"/><p>{{description}}</p>) — the default cell used when a grid-like controller needs asubcomponentClassand none is given. - ListComponent + ListItemComponent + ListController — vertical-list analogue
of the grid trio:
ListControllerdrivesListItemComponentinstances insideListComponentfromdata[]. - SliderComponent (name
"slider", shadowed) + SlideListComponent (name"slidelist", inline<p>Loading...</p>; forcescontrollerClass="DataGridController"and defaultssubcomponentClass="GridItemComponent") + SlideItemComponent (name"slider_item",Fadeeffect; inlineqcoSlidestemplate binding{{slideNumber}} {{__dataLength}} {{image}} {{title}} {{label}} {{link}} {{category}}, withslideNumber = __dataIndex + 1). - ModalEnclosureComponent / ModalComponent — modal shells (pair with ModalController).
- SwaggerUIComponent (+ SwaggerUIController) — injects Swagger-UI DOM.
- VideoSplashScreenComponent — video splash: first tag in the document,
data-background,data-video_mp4|_webm|_ogg,duration(example"5000"; absent-attribute default is1000),<img slot="logo">; main component follows withsplashscreenattr (<layout-basic splashscreen name="main" cached=true ...>in widget syntax). - SplashScreenComponent — base splash (extended by video + cube variants).
- CubeSplashScreenComponent — 3D spinning-cube splash (shadowed, inline
template with
spinkeyframes). Attributes:duration(example"3000"),data-background(any CSS background — gradients work,linear-gradient(…)),data-cube_image(face texture URL, bound to--background-3d-cube-image):<splash-screen componentClass="CubeSplashScreenComponent" duration="3000" data-background="linear-gradient(90deg, rgba(2,0,36,1) 0%, …)" data-cube_image="./img/Q_web-white.png"> </splash-screen> - NotificationComponent — notification shell. Drift note: registered under
the legacy
org.quickcorp.components.notificationspackage (the i18n loader also references anorg.quickcorp.*namespace); rename toorg.qcobjects.*when touched. - Visual assets live under
src/css+src/templates; class logic MUST NOT inline large CSS blobs.
Controllers catalogue¶
- GenericController — empty
Controllerextension point; extend it (instead of rawController) when a controller needs no built-in behavior yet. - GridController — CSS-only grid variant (does NOT pair with
GridComponent, which forcesDataGridController— see above). - DataGridController — maps
data[]ontosubcomponentClassinstances (e.g. profile cards:CardComponenttemplatecard.tpl.htmlwith{{profilePicture}} {{name}} {{email}}; list shellloading_list.tpl.html). - ListController — with ListComponent/ListItemComponent (see above).
- SliderController — autoplay for SliderComponent:
durationdefault 7100ms,slideIndex,interval; APIplusSlides(n),plusSlidesAndStop(n),currentSlide(n),stop(); shadow-aware (shadowRootwhen shadowed, elsebody); registers itself globally asslider_<instanceID>. - ModalController — modal behavior.
- FormValidations — instance validators:
(new FormValidations(o)).getDefault()returns a(fieldName, dataValue, element)=>boolchecker (name/email regexes or the element's ownpatternattribute). - FormController — 3-step forms: (1)
serviceClassstring (resolved via ClassFactory, may be fully qualified), (2)formSettings(backRoutingon fail /loadingRoutingwhile calling /nextRoutingon OK; defaults'#'/'#loading'/'#signupsuccessful'), (3)validations— an ARRAY-like keyed per field:validations[fieldName](fieldName, dataValue, element), NOT afield(){ return fn }wrapper shape.formSaveTouchHandlersubmits on click/touch of any.submitelement — override to change. ⚠️ KNOWN ISSUE:done()callsthis.onpress(".submit",…)butFormControlleroverridesonpresstothrow new Error("Method not implemented.")— submitting throughdone()throws until this is fixed; the safe-extension pattern (extendController, keep adefaulController = new FormController(o)wired in_new_(o), delegate indone()) is the workaround (see README signup example:SignupClientService extends JSONServicePOST +SignupFormController+ shadowedsignup-formtemplate with slots). - SwaggerUIController — with SwaggerUIComponent.
Effects catalogue (all requestAnimationFrame-based, CSS-smart)¶
Calling form matters — static-only vs instance-only is per class:
- Static
X.apply(el, …):Move,MoveXInFromRight/Left,MoveYInFromBottom/Top,RotateX,RotateY—(new Move()).applyisundefinedand throws. - Both forms:
Fadeonly (Fade.apply(el,aFrom,aTo)or(new Fade()).apply(…)). - Instance-only
(new X()).apply(el, …):RotateZ,Rotate(3D parallel, degrees 0–360),Radius,Resize(1 = normal),WipeLeft/Right/Up/Down. - Batch via
Tag(...).map(el => X.apply(el, …))for static classes,Tag(...).map(el => (new X()).apply(el, …))for instance classes. - Effects-dispatch pattern (reference: effects demo app): expose an
effects: {apply<Name>(el){…}}map on the controller plus anapplyEffect(name)dispatcher (this.effects["apply"+name](el)); generate trigger buttons with a custom meta processor emitting BOTHontouchstartandonclickhandlers (touch-first devices); register the controller inglobal(global.set("mainControllerInstance", this)indone()) so generated markup can reach it. Composed moves (slide/fall/rise) chain staticMove.applycalls with measured offsets (clientWidth/clientHeight); rotates SHOULD settransformOriginfirst. tplextensionis free-form: any extension value works (<name>.<tplextension>) — the TEMPLATE HANDLER class must support it. Text formats are natively supported (svg,md,txt— reference: clickable octocat viatplextension="svg"); non-text formats REQUIRE a custom handler that parses them (see 03-core-framework § Template handlers).
Modal presets (org.qcobjects.modal.effects — a REGISTERED package,
importable via ClassFactory("org.qcobjects.modal.effects.ModalFade");
only the npm subpath export is absent): ModalFade extends Fade
(500ms), ModalMoveUp extends Move (800ms), ModalMoveDown extends Move (300ms).
Models, cloud session, tools, views, i18n¶
org.qcobjects.models.Contact extends VO— canonical value-object example; extendVO(not plain objects) for model data.
Session handling (normative)¶
Sources: src/ts/org.qcobjects.cloud.auth.session.{usertoken,data}.ts, pinned at
https://github.com/QCObjects/qcobjects-sdk/blob/v2.5.105/src/ts/org.qcobjects.cloud.auth.session.usertoken.ts.
Status: beta (see 11-features) — API shape may still move.
- Token issuance (
SessionUserToken extends InheritClass): one singleton per username inglobalunderuserToken_<base64(username)>(getGlobalUser(...)creates-or-returns). The token is_Crypt.encrypt("userAgent|username|timestamp", origin-or-domain)held in aComplexStorageCachekeyed by instance ID (first access encrypts viaload, later accesses hitalternate/cache). Accessors:getGlobalUser{,Token,Id,Priority}(username). - Login credentials:
getLoginCredentialsToken(username, password)=_Crypt.encrypt(username+password, userToken)— the password never travels or persists raw; only the derived credential token leaves the client. - Logout:
closeGlobalSession(username)clears the token cache, nulls the global slot, and resetsSessionUserToken.userto{}. NOTE:ComplexStorageCache.clear()wipes ALLcachedObject_*keys — component and service caches go too, not just the token. - Session data (
SessionData extends InheritClass):sessionStorage-backed, keyedsession_<btoa(userToken)>so each login's data is namespaced by its token. (Theindex()missing-import guard is unreachable behind the static import — treat the import as mandatory, not the error.) A session container MUST be set first (setSessionContainer(...parts);getSessionContainer()throws when unset);save(...)stringifiessessionDatainto the slot,getSessionData(...)parses it back ({}when absent). - Rules: session reads/writes MUST go through these classes (never raw
sessionStoragekeys); tokens MUST NOT be logged; credential tokens MUST be re-derived per login, never stored; closing a session MUST clear both the token cache and thesessionStorageslot. org.qcobjects.tools.canvas.CanvasTool,org.qcobjects.tools.layouts.BasicLayout.org.qcobjects.tools.Process extends Timer— registry-only anonymous class; ⚠️ itsthread()override throwsMethod not implemented, sostart()always throws. Do not use until fixed.org.qcobjects.views.GridView(generic grid view).org.qcobjects.i18n_messages.i18n_messages— subclass per lang (class i18n_messages_es extends i18n_messageswithmessages:[{en,es}…]), instantiate and attach in the package (_i18n_messages_es: new i18n_messages_es()).
Verification¶
- Demo app renders each exported component with only core + SDK installed.
npm run buildregeneratesbuild/+public/;npm test(eslint + jasmine) green.- Full signup-form example (service + controller + shadowed templates) works verbatim.