snice 7.3.0 → 7.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agents/skills/snice/SKILL.md +12 -16
- package/README.md +5 -1
- package/adapters/react/SniceProvider.js +1 -1
- package/adapters/react/SniceRouter.js +1 -1
- package/adapters/react/matchRoute.js +1 -1
- package/adapters/react/useRequestHandler.js +1 -1
- package/bin/analyzer-contracts.js +3 -1
- package/bin/project-analyzer.js +219 -6
- package/bin/snice.js +77 -13
- package/dist/app-context.d.ts +10 -10
- package/dist/cdn/accordion/snice-accordion.js +1 -1
- package/dist/cdn/action-bar/snice-action-bar.js +1 -1
- package/dist/cdn/activity-feed/snice-activity-feed.js +1 -1
- package/dist/cdn/alert/snice-alert.js +1 -1
- package/dist/cdn/app-tiles/snice-app-tiles.js +1 -1
- package/dist/cdn/approval-flow/snice-approval-flow.js +1 -1
- package/dist/cdn/audio-recorder/snice-audio-recorder.js +1 -1
- package/dist/cdn/availability/snice-availability.js +1 -1
- package/dist/cdn/avatar/snice-avatar.js +1 -1
- package/dist/cdn/avatar-group/snice-avatar-group.js +1 -1
- package/dist/cdn/badge/snice-badge.js +1 -1
- package/dist/cdn/banner/snice-banner.js +1 -1
- package/dist/cdn/binpack/snice-binpack.js +1 -1
- package/dist/cdn/book/snice-book.js +1 -1
- package/dist/cdn/booking/snice-booking.js +1 -1
- package/dist/cdn/breadcrumbs/snice-breadcrumbs.js +1 -1
- package/dist/cdn/button/snice-button.js +1 -1
- package/dist/cdn/calendar/snice-calendar.js +1 -1
- package/dist/cdn/camera/snice-camera.js +1 -1
- package/dist/cdn/camera-annotate/snice-camera-annotate.js +1 -1
- package/dist/cdn/candlestick/snice-candlestick.js +1 -1
- package/dist/cdn/card/snice-card.js +1 -1
- package/dist/cdn/carousel/snice-carousel.js +1 -1
- package/dist/cdn/cart/snice-cart.js +1 -1
- package/dist/cdn/chart/snice-chart.js +1 -1
- package/dist/cdn/chat/snice-chat.js +1 -1
- package/dist/cdn/checkbox/snice-checkbox.js +1 -1
- package/dist/cdn/chip/snice-chip.js +1 -1
- package/dist/cdn/code-block/snice-code-block.js +1 -1
- package/dist/cdn/color-display/snice-color-display.js +1 -1
- package/dist/cdn/color-picker/snice-color-picker.js +1 -1
- package/dist/cdn/command-palette/snice-command-palette.js +1 -1
- package/dist/cdn/comments/snice-comments.js +1 -1
- package/dist/cdn/countdown/snice-countdown.js +1 -1
- package/dist/cdn/cropper/snice-cropper.js +1 -1
- package/dist/cdn/data-card/snice-data-card.js +1 -1
- package/dist/cdn/date-picker/snice-date-picker.js +1 -1
- package/dist/cdn/date-range-picker/snice-date-range-picker.js +1 -1
- package/dist/cdn/date-time-picker/snice-date-time-picker.js +1 -1
- package/dist/cdn/diff/snice-diff.js +1 -1
- package/dist/cdn/divider/snice-divider.js +1 -1
- package/dist/cdn/doc/snice-doc.js +1 -1
- package/dist/cdn/draw/snice-draw.js +1 -1
- package/dist/cdn/drawer/snice-drawer.js +1 -1
- package/dist/cdn/empty-state/snice-empty-state.js +1 -1
- package/dist/cdn/estimate/snice-estimate.js +1 -1
- package/dist/cdn/file-gallery/snice-file-gallery.js +1 -1
- package/dist/cdn/file-upload/snice-file-upload.js +1 -1
- package/dist/cdn/flip-card/snice-flip-card.js +1 -1
- package/dist/cdn/flow/snice-flow.js +1 -1
- package/dist/cdn/form-layout/snice-form-layout.js +1 -1
- package/dist/cdn/funnel/snice-funnel.js +1 -1
- package/dist/cdn/gantt/snice-gantt.js +1 -1
- package/dist/cdn/gauge/snice-gauge.js +1 -1
- package/dist/cdn/grid/snice-grid.js +1 -1
- package/dist/cdn/heatmap/snice-heatmap.js +1 -1
- package/dist/cdn/image/snice-image.js +1 -1
- package/dist/cdn/input/snice-input.js +1 -1
- package/dist/cdn/invoice/snice-invoice.js +1 -1
- package/dist/cdn/kanban/snice-kanban.js +1 -1
- package/dist/cdn/key-value/snice-key-value.js +1 -1
- package/dist/cdn/kpi/snice-kpi.js +1 -1
- package/dist/cdn/layout/snice-layout.js +1 -1
- package/dist/cdn/leaderboard/snice-leaderboard.js +1 -1
- package/dist/cdn/link/snice-link.js +1 -1
- package/dist/cdn/link-preview/snice-link-preview.js +1 -1
- package/dist/cdn/list/snice-list.js +1 -1
- package/dist/cdn/location/snice-location.js +1 -1
- package/dist/cdn/login/snice-login.js +1 -1
- package/dist/cdn/map/snice-map.js +1 -1
- package/dist/cdn/markdown/snice-markdown.js +1 -1
- package/dist/cdn/masonry/snice-masonry.js +1 -1
- package/dist/cdn/menu/snice-menu.js +1 -1
- package/dist/cdn/message-strip/snice-message-strip.js +1 -1
- package/dist/cdn/modal/snice-modal.js +1 -1
- package/dist/cdn/music-player/snice-music-player.js +1 -1
- package/dist/cdn/nav/snice-nav.js +1 -1
- package/dist/cdn/network-graph/snice-network-graph.js +1 -1
- package/dist/cdn/notification-center/snice-notification-center.js +1 -1
- package/dist/cdn/order-tracker/snice-order-tracker.js +1 -1
- package/dist/cdn/org-chart/snice-org-chart.js +1 -1
- package/dist/cdn/pagination/snice-pagination.js +1 -1
- package/dist/cdn/paint/snice-paint.js +1 -1
- package/dist/cdn/pdf-viewer/snice-pdf-viewer.js +1 -1
- package/dist/cdn/permission-matrix/snice-permission-matrix.js +1 -1
- package/dist/cdn/podcast-player/snice-podcast-player.js +1 -1
- package/dist/cdn/popover/snice-popover.js +1 -1
- package/dist/cdn/pricing-table/snice-pricing-table.js +1 -1
- package/dist/cdn/product-card/snice-product-card.js +1 -1
- package/dist/cdn/progress/snice-progress.js +1 -1
- package/dist/cdn/progress-ring/snice-progress-ring.js +1 -1
- package/dist/cdn/qr-code/snice-qr-code.js +1 -1
- package/dist/cdn/qr-reader/snice-qr-reader.js +1 -1
- package/dist/cdn/radio/snice-radio.js +1 -1
- package/dist/cdn/range-slider/snice-range-slider.js +1 -1
- package/dist/cdn/rating/snice-rating.js +1 -1
- package/dist/cdn/receipt/snice-receipt.js +1 -1
- package/dist/cdn/recipe/snice-recipe.js +1 -1
- package/dist/cdn/runtime/README.md +2 -2
- package/dist/cdn/runtime/snice-runtime.esm.js +309 -188
- package/dist/cdn/runtime/snice-runtime.esm.js.map +1 -1
- package/dist/cdn/runtime/snice-runtime.esm.min.js +1 -1
- package/dist/cdn/runtime/snice-runtime.esm.min.js.map +1 -1
- package/dist/cdn/runtime/snice-runtime.js +309 -187
- package/dist/cdn/runtime/snice-runtime.js.map +1 -1
- package/dist/cdn/runtime/snice-runtime.min.js +1 -1
- package/dist/cdn/runtime/snice-runtime.min.js.map +1 -1
- package/dist/cdn/sankey/snice-sankey.js +1 -1
- package/dist/cdn/segmented-control/snice-segmented-control.js +1 -1
- package/dist/cdn/select/snice-select.js +1 -1
- package/dist/cdn/skeleton/snice-skeleton.js +1 -1
- package/dist/cdn/slider/snice-slider.js +1 -1
- package/dist/cdn/sortable/snice-sortable.js +1 -1
- package/dist/cdn/sparkline/snice-sparkline.js +1 -1
- package/dist/cdn/spinner/snice-spinner.js +1 -1
- package/dist/cdn/split-button/snice-split-button.js +1 -1
- package/dist/cdn/split-pane/snice-split-pane.js +1 -1
- package/dist/cdn/spotlight/snice-spotlight.js +1 -1
- package/dist/cdn/stat-group/snice-stat-group.js +1 -1
- package/dist/cdn/step-input/snice-step-input.js +1 -1
- package/dist/cdn/stepper/snice-stepper.js +1 -1
- package/dist/cdn/switch/snice-switch.js +1 -1
- package/dist/cdn/table/snice-table.esm.js +1 -1
- package/dist/cdn/table/snice-table.js +1 -1
- package/dist/cdn/tabs/snice-tabs.js +1 -1
- package/dist/cdn/tag/snice-tag.js +1 -1
- package/dist/cdn/tag-input/snice-tag-input.js +1 -1
- package/dist/cdn/terminal/snice-terminal.js +1 -1
- package/dist/cdn/testimonial/snice-testimonial.js +1 -1
- package/dist/cdn/textarea/snice-textarea.js +1 -1
- package/dist/cdn/time-picker/snice-time-picker.js +1 -1
- package/dist/cdn/time-range-picker/snice-time-range-picker.js +1 -1
- package/dist/cdn/timeline/snice-timeline.js +1 -1
- package/dist/cdn/timer/snice-timer.js +1 -1
- package/dist/cdn/toast/snice-toast.js +1 -1
- package/dist/cdn/tooltip/snice-tooltip.js +1 -1
- package/dist/cdn/tree/snice-tree.js +1 -1
- package/dist/cdn/treemap/snice-treemap.js +1 -1
- package/dist/cdn/user-card/snice-user-card.js +1 -1
- package/dist/cdn/video-player/snice-video-player.js +1 -1
- package/dist/cdn/virtual-scroller/snice-virtual-scroller.js +1 -1
- package/dist/cdn/waterfall/snice-waterfall.js +1 -1
- package/dist/cdn/weather/snice-weather.js +1 -1
- package/dist/cdn/work-order/snice-work-order.js +1 -1
- package/dist/components/pagination/snice-pagination.types.d.ts +5 -0
- package/dist/context-provider.d.ts +11 -1
- package/dist/context.d.ts +5 -6
- package/dist/index.cjs +306 -184
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.esm.js +306 -185
- package/dist/index.esm.js.map +1 -1
- package/dist/index.iife.js +306 -184
- package/dist/index.iife.js.map +1 -1
- package/dist/react/SniceProvider.js +1 -1
- package/dist/react/SniceRouter.js +1 -1
- package/dist/react/index.js +1 -1
- package/dist/react/matchRoute.js +1 -1
- package/dist/react/useRequestHandler.js +1 -1
- package/dist/symbols.cjs +1 -1
- package/dist/symbols.esm.js +1 -1
- package/dist/testing-dom.cjs +48 -0
- package/dist/testing-dom.cjs.map +1 -0
- package/dist/testing-dom.d.ts +23 -0
- package/dist/testing-dom.esm.js +45 -0
- package/dist/testing-dom.esm.js.map +1 -0
- package/dist/transitions.cjs +1 -1
- package/dist/transitions.esm.js +1 -1
- package/dist/types/context.d.ts +8 -8
- package/dist/utils.d.ts +3 -1
- package/docs/ai/api.md +12 -1
- package/docs/ai/architecture.md +7 -3
- package/docs/ai/bindings.md +1 -1
- package/docs/ai/cli.md +22 -0
- package/docs/ai/components/badge.md +4 -0
- package/docs/ai/components/pagination.md +4 -2
- package/docs/ai/components/segmented-control.md +5 -1
- package/docs/ai/controllers.md +30 -11
- package/docs/ai/decorators.md +2 -2
- package/docs/ai/events.md +8 -2
- package/docs/ai/fetcher.md +32 -1
- package/docs/ai/lifecycle.md +4 -1
- package/docs/ai/patterns.md +9 -4
- package/docs/ai/request-response.md +20 -6
- package/docs/ai/routing.md +8 -1
- package/docs/ai/testing.md +71 -0
- package/docs/bindings.md +1 -1
- package/docs/cli.md +23 -0
- package/docs/components/badge.md +4 -0
- package/docs/components/pagination.md +8 -5
- package/docs/components/segmented-control.md +5 -1
- package/docs/controllers.md +30 -11
- package/docs/events.md +11 -2
- package/docs/fetcher.md +32 -1
- package/docs/lifecycle.md +5 -1
- package/docs/request-response.md +25 -7
- package/docs/routing.md +10 -1
- package/docs/testing.md +71 -0
- package/package.json +6 -1
package/dist/react/index.js
CHANGED
package/dist/react/matchRoute.js
CHANGED
package/dist/symbols.cjs
CHANGED
package/dist/symbols.esm.js
CHANGED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* snice v7.3.0
|
|
3
|
+
* A decorator-driven web component library with routing, controllers, daemons, and 130+ UI components. For better coding-agent results, run npx snice init-ai.
|
|
4
|
+
* (c) 2024
|
|
5
|
+
* Released under the MIT License.
|
|
6
|
+
*
|
|
7
|
+
* GENERATED FILE — DO NOT EDIT. Source: src/. Rebuild: npm run build:core
|
|
8
|
+
*/
|
|
9
|
+
'use strict';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Standards-shaped DOM compatibility for non-browser test runners.
|
|
13
|
+
*
|
|
14
|
+
* This module only fills missing IDL surface. It never replaces an existing
|
|
15
|
+
* implementation and does not attempt to simulate layout, paint, or browser
|
|
16
|
+
* focus navigation.
|
|
17
|
+
*/
|
|
18
|
+
/**
|
|
19
|
+
* Fill proven DOM-runner IDL gaps without replacing native implementations.
|
|
20
|
+
* Safe to call more than once.
|
|
21
|
+
*/
|
|
22
|
+
function installDOMTestingCompatibility(scope = globalThis) {
|
|
23
|
+
const installed = [];
|
|
24
|
+
const HTMLElementConstructor = scope.HTMLElement;
|
|
25
|
+
if (!HTMLElementConstructor)
|
|
26
|
+
return { installed };
|
|
27
|
+
const prototype = HTMLElementConstructor.prototype;
|
|
28
|
+
if (!('autofocus' in prototype)) {
|
|
29
|
+
Object.defineProperty(prototype, 'autofocus', {
|
|
30
|
+
configurable: true,
|
|
31
|
+
enumerable: true,
|
|
32
|
+
get() {
|
|
33
|
+
return this.hasAttribute('autofocus');
|
|
34
|
+
},
|
|
35
|
+
set(value) {
|
|
36
|
+
this.toggleAttribute('autofocus', Boolean(value));
|
|
37
|
+
},
|
|
38
|
+
});
|
|
39
|
+
installed.push('HTMLElement.autofocus');
|
|
40
|
+
}
|
|
41
|
+
return { installed };
|
|
42
|
+
}
|
|
43
|
+
/** Result of the side-effect installation performed by `snice/testing/dom`. */
|
|
44
|
+
const domTestingCompatibility = installDOMTestingCompatibility();
|
|
45
|
+
|
|
46
|
+
exports.domTestingCompatibility = domTestingCompatibility;
|
|
47
|
+
exports.installDOMTestingCompatibility = installDOMTestingCompatibility;
|
|
48
|
+
//# sourceMappingURL=testing-dom.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"testing-dom.cjs","sources":["../src/testing-dom.ts"],"sourcesContent":["/**\n * Standards-shaped DOM compatibility for non-browser test runners.\n *\n * This module only fills missing IDL surface. It never replaces an existing\n * implementation and does not attempt to simulate layout, paint, or browser\n * focus navigation.\n */\n\nexport type DOMTestingCompatibilityFeature = 'HTMLElement.autofocus';\n\nexport interface DOMTestingCompatibilityReport {\n /** Standards features installed because the current DOM omitted them. */\n installed: readonly DOMTestingCompatibilityFeature[];\n}\n\ninterface DOMTestingGlobal {\n HTMLElement?: typeof HTMLElement;\n}\n\n/**\n * Fill proven DOM-runner IDL gaps without replacing native implementations.\n * Safe to call more than once.\n */\nexport function installDOMTestingCompatibility(\n scope: DOMTestingGlobal = globalThis as DOMTestingGlobal\n): DOMTestingCompatibilityReport {\n const installed: DOMTestingCompatibilityFeature[] = [];\n const HTMLElementConstructor = scope.HTMLElement;\n if (!HTMLElementConstructor) return { installed };\n\n const prototype = HTMLElementConstructor.prototype;\n if (!('autofocus' in prototype)) {\n Object.defineProperty(prototype, 'autofocus', {\n configurable: true,\n enumerable: true,\n get(this: HTMLElement): boolean {\n return this.hasAttribute('autofocus');\n },\n set(this: HTMLElement, value: boolean) {\n this.toggleAttribute('autofocus', Boolean(value));\n },\n });\n installed.push('HTMLElement.autofocus');\n }\n\n return { installed };\n}\n\n/** Result of the side-effect installation performed by `snice/testing/dom`. */\nexport const domTestingCompatibility = installDOMTestingCompatibility();\n"],"names":[],"mappings":";;;;;;;;;;AAAA;;;;;;AAMG;AAaH;;;AAGG;AACG,SAAU,8BAA8B,CAC5C,KAAA,GAA0B,UAA8B,EAAA;IAExD,MAAM,SAAS,GAAqC,EAAE;AACtD,IAAA,MAAM,sBAAsB,GAAG,KAAK,CAAC,WAAW;AAChD,IAAA,IAAI,CAAC,sBAAsB;QAAE,OAAO,EAAE,SAAS,EAAE;AAEjD,IAAA,MAAM,SAAS,GAAG,sBAAsB,CAAC,SAAS;AAClD,IAAA,IAAI,EAAE,WAAW,IAAI,SAAS,CAAC,EAAE;AAC/B,QAAA,MAAM,CAAC,cAAc,CAAC,SAAS,EAAE,WAAW,EAAE;AAC5C,YAAA,YAAY,EAAE,IAAI;AAClB,YAAA,UAAU,EAAE,IAAI;YAChB,GAAG,GAAA;AACD,gBAAA,OAAO,IAAI,CAAC,YAAY,CAAC,WAAW,CAAC;YACvC,CAAC;AACD,YAAA,GAAG,CAAoB,KAAc,EAAA;gBACnC,IAAI,CAAC,eAAe,CAAC,WAAW,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC;YACnD,CAAC;AACF,SAAA,CAAC;AACF,QAAA,SAAS,CAAC,IAAI,CAAC,uBAAuB,CAAC;IACzC;IAEA,OAAO,EAAE,SAAS,EAAE;AACtB;AAEA;AACO,MAAM,uBAAuB,GAAG,8BAA8B;;;;;"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Standards-shaped DOM compatibility for non-browser test runners.
|
|
3
|
+
*
|
|
4
|
+
* This module only fills missing IDL surface. It never replaces an existing
|
|
5
|
+
* implementation and does not attempt to simulate layout, paint, or browser
|
|
6
|
+
* focus navigation.
|
|
7
|
+
*/
|
|
8
|
+
export type DOMTestingCompatibilityFeature = 'HTMLElement.autofocus';
|
|
9
|
+
export interface DOMTestingCompatibilityReport {
|
|
10
|
+
/** Standards features installed because the current DOM omitted them. */
|
|
11
|
+
installed: readonly DOMTestingCompatibilityFeature[];
|
|
12
|
+
}
|
|
13
|
+
interface DOMTestingGlobal {
|
|
14
|
+
HTMLElement?: typeof HTMLElement;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Fill proven DOM-runner IDL gaps without replacing native implementations.
|
|
18
|
+
* Safe to call more than once.
|
|
19
|
+
*/
|
|
20
|
+
export declare function installDOMTestingCompatibility(scope?: DOMTestingGlobal): DOMTestingCompatibilityReport;
|
|
21
|
+
/** Result of the side-effect installation performed by `snice/testing/dom`. */
|
|
22
|
+
export declare const domTestingCompatibility: DOMTestingCompatibilityReport;
|
|
23
|
+
export {};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* snice v7.3.0
|
|
3
|
+
* A decorator-driven web component library with routing, controllers, daemons, and 130+ UI components. For better coding-agent results, run npx snice init-ai.
|
|
4
|
+
* (c) 2024
|
|
5
|
+
* Released under the MIT License.
|
|
6
|
+
*
|
|
7
|
+
* GENERATED FILE — DO NOT EDIT. Source: src/. Rebuild: npm run build:core
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Standards-shaped DOM compatibility for non-browser test runners.
|
|
11
|
+
*
|
|
12
|
+
* This module only fills missing IDL surface. It never replaces an existing
|
|
13
|
+
* implementation and does not attempt to simulate layout, paint, or browser
|
|
14
|
+
* focus navigation.
|
|
15
|
+
*/
|
|
16
|
+
/**
|
|
17
|
+
* Fill proven DOM-runner IDL gaps without replacing native implementations.
|
|
18
|
+
* Safe to call more than once.
|
|
19
|
+
*/
|
|
20
|
+
function installDOMTestingCompatibility(scope = globalThis) {
|
|
21
|
+
const installed = [];
|
|
22
|
+
const HTMLElementConstructor = scope.HTMLElement;
|
|
23
|
+
if (!HTMLElementConstructor)
|
|
24
|
+
return { installed };
|
|
25
|
+
const prototype = HTMLElementConstructor.prototype;
|
|
26
|
+
if (!('autofocus' in prototype)) {
|
|
27
|
+
Object.defineProperty(prototype, 'autofocus', {
|
|
28
|
+
configurable: true,
|
|
29
|
+
enumerable: true,
|
|
30
|
+
get() {
|
|
31
|
+
return this.hasAttribute('autofocus');
|
|
32
|
+
},
|
|
33
|
+
set(value) {
|
|
34
|
+
this.toggleAttribute('autofocus', Boolean(value));
|
|
35
|
+
},
|
|
36
|
+
});
|
|
37
|
+
installed.push('HTMLElement.autofocus');
|
|
38
|
+
}
|
|
39
|
+
return { installed };
|
|
40
|
+
}
|
|
41
|
+
/** Result of the side-effect installation performed by `snice/testing/dom`. */
|
|
42
|
+
const domTestingCompatibility = installDOMTestingCompatibility();
|
|
43
|
+
|
|
44
|
+
export { domTestingCompatibility, installDOMTestingCompatibility };
|
|
45
|
+
//# sourceMappingURL=testing-dom.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"testing-dom.esm.js","sources":["../src/testing-dom.ts"],"sourcesContent":["/**\n * Standards-shaped DOM compatibility for non-browser test runners.\n *\n * This module only fills missing IDL surface. It never replaces an existing\n * implementation and does not attempt to simulate layout, paint, or browser\n * focus navigation.\n */\n\nexport type DOMTestingCompatibilityFeature = 'HTMLElement.autofocus';\n\nexport interface DOMTestingCompatibilityReport {\n /** Standards features installed because the current DOM omitted them. */\n installed: readonly DOMTestingCompatibilityFeature[];\n}\n\ninterface DOMTestingGlobal {\n HTMLElement?: typeof HTMLElement;\n}\n\n/**\n * Fill proven DOM-runner IDL gaps without replacing native implementations.\n * Safe to call more than once.\n */\nexport function installDOMTestingCompatibility(\n scope: DOMTestingGlobal = globalThis as DOMTestingGlobal\n): DOMTestingCompatibilityReport {\n const installed: DOMTestingCompatibilityFeature[] = [];\n const HTMLElementConstructor = scope.HTMLElement;\n if (!HTMLElementConstructor) return { installed };\n\n const prototype = HTMLElementConstructor.prototype;\n if (!('autofocus' in prototype)) {\n Object.defineProperty(prototype, 'autofocus', {\n configurable: true,\n enumerable: true,\n get(this: HTMLElement): boolean {\n return this.hasAttribute('autofocus');\n },\n set(this: HTMLElement, value: boolean) {\n this.toggleAttribute('autofocus', Boolean(value));\n },\n });\n installed.push('HTMLElement.autofocus');\n }\n\n return { installed };\n}\n\n/** Result of the side-effect installation performed by `snice/testing/dom`. */\nexport const domTestingCompatibility = installDOMTestingCompatibility();\n"],"names":[],"mappings":";;;;;;;;AAAA;;;;;;AAMG;AAaH;;;AAGG;AACG,SAAU,8BAA8B,CAC5C,KAAA,GAA0B,UAA8B,EAAA;IAExD,MAAM,SAAS,GAAqC,EAAE;AACtD,IAAA,MAAM,sBAAsB,GAAG,KAAK,CAAC,WAAW;AAChD,IAAA,IAAI,CAAC,sBAAsB;QAAE,OAAO,EAAE,SAAS,EAAE;AAEjD,IAAA,MAAM,SAAS,GAAG,sBAAsB,CAAC,SAAS;AAClD,IAAA,IAAI,EAAE,WAAW,IAAI,SAAS,CAAC,EAAE;AAC/B,QAAA,MAAM,CAAC,cAAc,CAAC,SAAS,EAAE,WAAW,EAAE;AAC5C,YAAA,YAAY,EAAE,IAAI;AAClB,YAAA,UAAU,EAAE,IAAI;YAChB,GAAG,GAAA;AACD,gBAAA,OAAO,IAAI,CAAC,YAAY,CAAC,WAAW,CAAC;YACvC,CAAC;AACD,YAAA,GAAG,CAAoB,KAAc,EAAA;gBACnC,IAAI,CAAC,eAAe,CAAC,WAAW,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC;YACnD,CAAC;AACF,SAAA,CAAC;AACF,QAAA,SAAS,CAAC,IAAI,CAAC,uBAAuB,CAAC;IACzC;IAEA,OAAO,EAAE,SAAS,EAAE;AACtB;AAEA;AACO,MAAM,uBAAuB,GAAG,8BAA8B;;;;"}
|
package/dist/transitions.cjs
CHANGED
package/dist/transitions.esm.js
CHANGED
package/dist/types/context.d.ts
CHANGED
|
@@ -9,21 +9,21 @@ declare const REGISTERED_ELEMENTS_SET: unique symbol;
|
|
|
9
9
|
* Represents the bundled router state that can notify registered elements of changes
|
|
10
10
|
*/
|
|
11
11
|
export declare class Context {
|
|
12
|
-
[REGISTERED_ELEMENTS]: WeakMap<
|
|
12
|
+
[REGISTERED_ELEMENTS]: WeakMap<object, Set<string>>;
|
|
13
13
|
[IS_UPDATING]: boolean;
|
|
14
14
|
/**
|
|
15
|
-
* Register an element to receive context updates
|
|
15
|
+
* Register an element or controller to receive context updates
|
|
16
16
|
* @internal Used by @context decorator
|
|
17
17
|
*/
|
|
18
|
-
[CONTEXT_REGISTER]: (element:
|
|
18
|
+
[CONTEXT_REGISTER]: (element: object, methodName: string) => void;
|
|
19
19
|
/**
|
|
20
|
-
* Unregister
|
|
20
|
+
* Unregister a subscriber from context updates. With a methodName,
|
|
21
21
|
* removes only that one handler (so a `once` handler tearing itself down
|
|
22
22
|
* doesn't kill the element's other @context handlers); without one, removes
|
|
23
|
-
* the
|
|
23
|
+
* the subscriber entirely (used on disconnect/detach).
|
|
24
24
|
* @internal Used by @context decorator cleanup
|
|
25
25
|
*/
|
|
26
|
-
[CONTEXT_UNREGISTER]: (element:
|
|
26
|
+
[CONTEXT_UNREGISTER]: (element: object, methodName?: string) => void;
|
|
27
27
|
/**
|
|
28
28
|
* Update the context and notify all registered elements
|
|
29
29
|
* Prevents infinite loops by tracking update state
|
|
@@ -31,10 +31,10 @@ export declare class Context {
|
|
|
31
31
|
*/
|
|
32
32
|
[CONTEXT_UPDATE]: (context: AppContext, placards: Placard[], currentRoute: string, routeParams: RouteParams) => void;
|
|
33
33
|
/**
|
|
34
|
-
* Notify a specific
|
|
34
|
+
* Notify a specific subscriber of the current context state
|
|
35
35
|
* @internal Used by @context decorator
|
|
36
36
|
*/
|
|
37
|
-
[CONTEXT_NOTIFY_ELEMENT]: (element:
|
|
37
|
+
[CONTEXT_NOTIFY_ELEMENT]: (element: object) => void;
|
|
38
38
|
private [REGISTERED_ELEMENTS_SET];
|
|
39
39
|
/**
|
|
40
40
|
* Unique immutable identifier for this context instance
|
package/dist/utils.d.ts
CHANGED
|
@@ -101,7 +101,9 @@ export declare function invokeImmediateWatchers(instance: any, constructor: any)
|
|
|
101
101
|
/**
|
|
102
102
|
* Create a debounced version of a function
|
|
103
103
|
*/
|
|
104
|
-
export declare function createDebounced<T extends (...args: any[]) => any>(fn: T, delay: number): T
|
|
104
|
+
export declare function createDebounced<T extends (...args: any[]) => any>(fn: T, delay: number): T & {
|
|
105
|
+
cancel(): void;
|
|
106
|
+
};
|
|
105
107
|
/**
|
|
106
108
|
* Create a throttled version of a function (leading + trailing edge)
|
|
107
109
|
*/
|
package/docs/ai/api.md
CHANGED
|
@@ -122,7 +122,7 @@ html`
|
|
|
122
122
|
@dispatch(eventName: string, options?: { debounce?, throttle?, dispatchOnUndefined?, scope?, daemon?, ...EventInit })
|
|
123
123
|
// Fires CustomEvent after method, detail = return value
|
|
124
124
|
// Supports async methods (dispatches after promise resolves)
|
|
125
|
-
// dispatchOnUndefined: false
|
|
125
|
+
// dispatchOnUndefined: false — skips when return is undefined; omitted/true still dispatches
|
|
126
126
|
// scope: 'global' | selector | EventTarget | () => EventTarget | null — redirects dispatch target
|
|
127
127
|
```
|
|
128
128
|
|
|
@@ -178,6 +178,8 @@ html`
|
|
|
178
178
|
// Example:
|
|
179
179
|
// @respond('fetch-user')
|
|
180
180
|
// async handleFetchUser(payload: { id: string }) {
|
|
181
|
+
// const fetch = getContextFetch(this);
|
|
182
|
+
// if (!fetch) throw new Error('Controller requires context fetch');
|
|
181
183
|
// const user = await fetch(`/api/users/${payload.id}`).then(r => r.json());
|
|
182
184
|
// return user;
|
|
183
185
|
// }
|
|
@@ -191,6 +193,14 @@ const context = getContext(elementOrController);
|
|
|
191
193
|
// Provide before connect/attach. Call release() during app/test teardown.
|
|
192
194
|
```
|
|
193
195
|
|
|
196
|
+
Context transport:
|
|
197
|
+
```typescript
|
|
198
|
+
const release = provideContext(appRoot, appContext, { fetch: appFetch });
|
|
199
|
+
const fetch = getContextFetch(elementOrController);
|
|
200
|
+
// Router supplies its ContextAwareFetcher-bound Context.fetch automatically.
|
|
201
|
+
// AppContext is not mutated with a reserved transport key.
|
|
202
|
+
```
|
|
203
|
+
|
|
194
204
|
## Observers
|
|
195
205
|
|
|
196
206
|
```typescript
|
|
@@ -222,6 +232,7 @@ const context = getContext(elementOrController);
|
|
|
222
232
|
|
|
223
233
|
@context(options?: { debounce?, throttle?, once? })
|
|
224
234
|
// Method decorator: receives Context on navigation and ctx.update() calls
|
|
235
|
+
// Supported on pages, descendant elements, and attached controllers.
|
|
225
236
|
// Called on: initial load, route change, ctx.update()
|
|
226
237
|
// Example:
|
|
227
238
|
// @context() handleContext(ctx: Context) {
|
package/docs/ai/architecture.md
CHANGED
|
@@ -103,8 +103,9 @@ decomposition suggestions.
|
|
|
103
103
|
2. ControllerPart commit / attributeChangedCallback detects change
|
|
104
104
|
3. Detach old controller (if any)
|
|
105
105
|
4. Attach new controller (class refs skip the registry; reference-deduped)
|
|
106
|
-
5.
|
|
107
|
-
6.
|
|
106
|
+
5. Register `@context` and catch up with the current Router context
|
|
107
|
+
6. Set up `@observe`, `@respond`, and `@on` handlers
|
|
108
|
+
7. On detach: clean up every managed decorator
|
|
108
109
|
- Class bindings own the element; their decorator name is reflected as a diagnostic-only `controller="name"` marker
|
|
109
110
|
- Native elements: class bindings attach via ControllerPart; string attrs via MutationObserver
|
|
110
111
|
|
|
@@ -184,9 +185,12 @@ async *fetchData(): Response<Data> {
|
|
|
184
185
|
}
|
|
185
186
|
|
|
186
187
|
// Controller responds (receives payload, returns result directly)
|
|
188
|
+
private ctx!: Context;
|
|
189
|
+
@context() receiveContext(ctx: Context) { this.ctx = ctx; }
|
|
190
|
+
|
|
187
191
|
@respond('fetch-data')
|
|
188
192
|
async handleFetch(payload: { id: string }) {
|
|
189
|
-
return await fetch(`/api/${payload.id}`).then(r => r.json());
|
|
193
|
+
return await this.ctx.fetch(`/api/${payload.id}`).then(r => r.json());
|
|
190
194
|
}
|
|
191
195
|
// Wiring: html`<my-element controller=${MyController}></my-element>` (or controller="my-controller" in raw HTML)
|
|
192
196
|
```
|
package/docs/ai/bindings.md
CHANGED
|
@@ -48,7 +48,7 @@ Ordinary attributes/comments support multiple expressions. Property, boolean, ev
|
|
|
48
48
|
- Direct JS assignment; preserves type and identity.
|
|
49
49
|
- null/undefined/false assigned unchanged; `nothing` assigns undefined; `noChange` skips.
|
|
50
50
|
- Same committed value skips write.
|
|
51
|
-
- `live(value)` compares value to the current DOM property instead of last commit; use only in property bindings when controlled native state must be reasserted after
|
|
51
|
+
- `live(value)` compares value to the current DOM property instead of last commit; use only in property bindings when controlled native or custom-element state must be reasserted after the user or element mutates it.
|
|
52
52
|
- `live()` does not observe DOM mutations or schedule a render. It reasserts only when the owning template renders. A modal open-state change can supply that render when reopening should restore an unchanged seed value.
|
|
53
53
|
|
|
54
54
|
```typescript
|
package/docs/ai/cli.md
CHANGED
|
@@ -59,6 +59,28 @@ Non-blocking architecture suggestions:
|
|
|
59
59
|
|
|
60
60
|
Both `doctor` and `validate` accept `--json`.
|
|
61
61
|
|
|
62
|
+
## Diagnostic codes and `.sniceignore`
|
|
63
|
+
|
|
64
|
+
Every error, warning, and suggestion has a stable `code` in JSON and text
|
|
65
|
+
output. Create `.sniceignore` at the project root to suppress an accepted
|
|
66
|
+
diagnostic. Entries are exact; glob patterns are not used.
|
|
67
|
+
|
|
68
|
+
```text
|
|
69
|
+
# One exact diagnostic (preferred)
|
|
70
|
+
snice/prefer-dispatch-decorator src/components/legacy-filter.ts:42:5
|
|
71
|
+
|
|
72
|
+
# Every instance in one file
|
|
73
|
+
snice/prefer-dispatch-decorator src/components/legacy-filter.ts
|
|
74
|
+
|
|
75
|
+
# The rule everywhere in the project
|
|
76
|
+
snice/prefer-dispatch-decorator
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
`code path:line` is also accepted. Paths are project-relative. A code-only
|
|
80
|
+
entry suppresses the rule globally, including its effect on the command's exit
|
|
81
|
+
status; use it only when the project intentionally rejects that rule. The same
|
|
82
|
+
file applies to `check`, `doctor`, and `validate`.
|
|
83
|
+
|
|
62
84
|
## init-ai
|
|
63
85
|
|
|
64
86
|
```bash
|
|
@@ -19,6 +19,10 @@ showZero: boolean = false; // attr: show-zero — render 0 instead of h
|
|
|
19
19
|
offset: number = 0;
|
|
20
20
|
```
|
|
21
21
|
|
|
22
|
+
The indicator is hidden when there is no dot, non-empty `content`, positive
|
|
23
|
+
`count`, or `showZero`. Slotted content remains available as the element the
|
|
24
|
+
badge would overlay.
|
|
25
|
+
|
|
22
26
|
## Methods
|
|
23
27
|
|
|
24
28
|
- `setBadgeContent(content)` - Set text content
|
|
@@ -57,9 +57,11 @@ variant: 'default'|'rounded'|'text' = 'default';
|
|
|
57
57
|
|
|
58
58
|
```typescript
|
|
59
59
|
import 'snice/components/pagination/snice-pagination';
|
|
60
|
+
import type { PaginationChangeDetail } from 'snice/components/pagination/snice-pagination.types';
|
|
60
61
|
|
|
61
|
-
pagination.addEventListener('pagination-change', (
|
|
62
|
-
|
|
62
|
+
pagination.addEventListener('pagination-change', (event) => {
|
|
63
|
+
const { detail } = event as CustomEvent<PaginationChangeDetail>;
|
|
64
|
+
console.log('Page:', detail.page);
|
|
63
65
|
});
|
|
64
66
|
```
|
|
65
67
|
|
|
@@ -5,7 +5,7 @@ Multi-option switcher with sliding indicator. One selected at a time.
|
|
|
5
5
|
## Properties
|
|
6
6
|
|
|
7
7
|
```typescript
|
|
8
|
-
value: string = '';
|
|
8
|
+
value: string = ''; // user selection updates this before value-change dispatches
|
|
9
9
|
options: SegmentedControlOption[] = []; // JS only: { value, label, icon?, disabled? }
|
|
10
10
|
size: 'small'|'medium'|'large' = 'medium';
|
|
11
11
|
disabled: boolean = false;
|
|
@@ -15,6 +15,10 @@ disabled: boolean = false;
|
|
|
15
15
|
|
|
16
16
|
- `value-change` → `{ value: string, previousValue: string, option: SegmentedControlOption, control }`
|
|
17
17
|
|
|
18
|
+
`value` is self-mutating: selecting an option assigns it before the event is
|
|
19
|
+
dispatched. An owner that must reassert an unchanged value on a later render
|
|
20
|
+
should use a `.value=${live(value)}` property binding.
|
|
21
|
+
|
|
18
22
|
## CSS Parts
|
|
19
23
|
|
|
20
24
|
- `base` - Root container
|
package/docs/ai/controllers.md
CHANGED
|
@@ -75,13 +75,14 @@ el.controller = UserController; // snice elements
|
|
|
75
75
|
**Attachment flow:**
|
|
76
76
|
1. Controller instance created
|
|
77
77
|
2. `element` property set
|
|
78
|
-
3. Router context passed (if available)
|
|
78
|
+
3. Router application context passed (if available)
|
|
79
79
|
4. Element's `ready` promise awaited
|
|
80
80
|
5. `attach()` called
|
|
81
|
-
6.
|
|
82
|
-
7.
|
|
83
|
-
8.
|
|
84
|
-
9.
|
|
81
|
+
6. `@context` handlers registered and caught up with the current Router context
|
|
82
|
+
7. Observers set up
|
|
83
|
+
8. Channel/response handlers set up
|
|
84
|
+
9. Event handlers set up
|
|
85
|
+
10. `controller-attached` event dispatched
|
|
85
86
|
|
|
86
87
|
Exception to step 4: `await attachController(this, ControllerClass)` inside the
|
|
87
88
|
host's own `@ready` handler attaches immediately because initial render is
|
|
@@ -96,8 +97,9 @@ good architecture; pages should orchestrate directly.
|
|
|
96
97
|
3. Observers cleaned up
|
|
97
98
|
4. Channel/response handlers cleaned up
|
|
98
99
|
5. Event handlers cleaned up
|
|
99
|
-
6.
|
|
100
|
-
7.
|
|
100
|
+
6. `@context` handlers cleaned up
|
|
101
|
+
7. Controller scope cleaned up
|
|
102
|
+
8. `controller-detached` event dispatched
|
|
101
103
|
|
|
102
104
|
## Native Element Controllers
|
|
103
105
|
|
|
@@ -113,7 +115,7 @@ Controllers provide specific behaviors (fetch/sort/filter) to generic visual com
|
|
|
113
115
|
|
|
114
116
|
## Resource Cleanup
|
|
115
117
|
|
|
116
|
-
Framework auto-cleans `@on`, `@observe`, `@respond` handlers. Clean up your own resources (WebSockets, timers, manual listeners) in `detach`:
|
|
118
|
+
Framework auto-cleans `@on`, `@observe`, `@respond`, and `@context` handlers. Clean up your own resources (WebSockets, timers, manual listeners) in `detach`:
|
|
117
119
|
|
|
118
120
|
```typescript
|
|
119
121
|
@controller('resource-controller')
|
|
@@ -192,6 +194,8 @@ the element's reactive public API. Dispatch outcome events, never mutate the
|
|
|
192
194
|
element's rendered DOM.
|
|
193
195
|
|
|
194
196
|
```typescript
|
|
197
|
+
import { context, type Context } from 'snice';
|
|
198
|
+
|
|
195
199
|
interface DataHost<T> extends HTMLElement {
|
|
196
200
|
loading: boolean;
|
|
197
201
|
error: string;
|
|
@@ -202,17 +206,24 @@ interface DataHost<T> extends HTMLElement {
|
|
|
202
206
|
@controller('data-controller')
|
|
203
207
|
class DataController<T> implements IController<DataHost<T>> {
|
|
204
208
|
element: DataHost<T> | null = null;
|
|
209
|
+
private ctx?: Context;
|
|
205
210
|
private abort?: AbortController;
|
|
206
211
|
private version = 0;
|
|
207
212
|
|
|
208
|
-
|
|
213
|
+
attach(element: DataHost<T>) {
|
|
209
214
|
this.element = element;
|
|
210
|
-
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
@context()
|
|
218
|
+
receiveContext(ctx: Context) {
|
|
219
|
+
this.ctx = ctx;
|
|
220
|
+
void this.reload();
|
|
211
221
|
}
|
|
212
222
|
|
|
213
223
|
async detach() {
|
|
214
224
|
this.version++;
|
|
215
225
|
this.abort?.abort();
|
|
226
|
+
this.ctx = undefined;
|
|
216
227
|
}
|
|
217
228
|
|
|
218
229
|
async reload() {
|
|
@@ -226,7 +237,9 @@ class DataController<T> implements IController<DataHost<T>> {
|
|
|
226
237
|
host.empty = false;
|
|
227
238
|
|
|
228
239
|
try {
|
|
229
|
-
const
|
|
240
|
+
const ctx = this.ctx;
|
|
241
|
+
if (!ctx) throw new Error('DataController requires Router context');
|
|
242
|
+
const response = await ctx.fetch('/api/data', { signal: abort.signal });
|
|
230
243
|
if (!response.ok) throw new Error(`Request failed (${response.status})`);
|
|
231
244
|
const data = await response.json() as T[];
|
|
232
245
|
if (version !== this.version || this.element !== host) return;
|
|
@@ -251,6 +264,12 @@ class DataController<T> implements IController<DataHost<T>> {
|
|
|
251
264
|
}
|
|
252
265
|
```
|
|
253
266
|
|
|
267
|
+
`@context()` works on controllers as well as elements. It receives the same
|
|
268
|
+
long-lived `Context` instance, including `application`, navigation state, and
|
|
269
|
+
the Router's middleware-aware `fetch`. Managed decorators are activated after
|
|
270
|
+
`attach()`, so start context-dependent work in the `@context()` handler (or in
|
|
271
|
+
an event handled later), not in `attach()`.
|
|
272
|
+
|
|
254
273
|
Required pieces: abort previous work, increment a version for stale-response
|
|
255
274
|
guarding, reset loading/error/empty before fetch, check version + host identity
|
|
256
275
|
before every commit, expose retry as `reload()`, and render all four states in
|
package/docs/ai/decorators.md
CHANGED
|
@@ -66,8 +66,8 @@ Resolver re-runs on reconnect — listeners track DOM moves.
|
|
|
66
66
|
## Communication
|
|
67
67
|
- `@request(channel, options?)` - Async generator request pattern; `{ daemon: 'name' }` targets an app-context daemon
|
|
68
68
|
- `@respond(channel, options?)` - Handle requests from `@request`; `{ daemon: 'name' }` installs on that daemon target
|
|
69
|
-
- `@context(options?)` - Receive router navigation context updates
|
|
70
|
-
- `provideContext(root, context)` / `getContext(participant)` - Explicit
|
|
69
|
+
- `@context(options?)` - Receive router navigation context updates on pages, descendant elements, and attached controllers
|
|
70
|
+
- `provideContext(root, context, { fetch? })` / `getContext(participant)` / `getContextFetch(participant)` - Explicit app-context and transport provisioning/lookup
|
|
71
71
|
|
|
72
72
|
## Observers
|
|
73
73
|
- `@observe(target, selector?, options?)` - Watch intersection, resize, media query, mutation
|
package/docs/ai/events.md
CHANGED
|
@@ -272,7 +272,7 @@ updateStatus(status: string) {
|
|
|
272
272
|
|
|
273
273
|
```typescript
|
|
274
274
|
interface DispatchOptions extends EventInit {
|
|
275
|
-
dispatchOnUndefined?: boolean; //
|
|
275
|
+
dispatchOnUndefined?: boolean; // Undefined return still dispatches unless false (default: true)
|
|
276
276
|
debounce?: number; // Debounce dispatch by ms
|
|
277
277
|
throttle?: number; // Throttle dispatch by ms
|
|
278
278
|
// Where to dispatch the event (see scope below)
|
|
@@ -379,7 +379,13 @@ methods themselves default to their own private target. See
|
|
|
379
379
|
|
|
380
380
|
## Custom events
|
|
381
381
|
|
|
382
|
-
|
|
382
|
+
Prefer `@dispatch` for a static custom event emitted from a Snice element or a
|
|
383
|
+
controller's host. It supplies the standard bubbling/composed behavior, uses
|
|
384
|
+
the method return value as `detail`, and keeps event timing options declarative.
|
|
385
|
+
|
|
386
|
+
Manual dispatch remains a valid low-level escape hatch when code needs the
|
|
387
|
+
Event object, a dynamic event name, `dispatchEvent()`'s cancellation boolean,
|
|
388
|
+
or a non-host target:
|
|
383
389
|
|
|
384
390
|
```typescript
|
|
385
391
|
notify() {
|
package/docs/ai/fetcher.md
CHANGED
|
@@ -30,7 +30,13 @@ const router = Router({ target: '#app', context: { auth: null }, fetcher });
|
|
|
30
30
|
router.initialize();
|
|
31
31
|
```
|
|
32
32
|
|
|
33
|
-
|
|
33
|
+
Pages, descendant elements, and attached controllers receive the Router's
|
|
34
|
+
long-lived `Context` through `@context()`; its `ctx.fetch` is the bound,
|
|
35
|
+
middleware-aware function. `getContextFetch(this)` remains the lower-level
|
|
36
|
+
lookup for code using an explicit non-router
|
|
37
|
+
`provideContext(root, appContext, { fetch })` boundary.
|
|
38
|
+
|
|
39
|
+
## `ctx.fetch` in pages, elements, and controllers
|
|
34
40
|
|
|
35
41
|
Available on any `Context` obtained via `@context()`; middleware applies automatically:
|
|
36
42
|
|
|
@@ -45,6 +51,31 @@ class UserPage extends HTMLElement {
|
|
|
45
51
|
}
|
|
46
52
|
```
|
|
47
53
|
|
|
54
|
+
Controllers use the same decorator. Managed decorators are activated after
|
|
55
|
+
`attach()`, so start context-dependent work from the handler:
|
|
56
|
+
|
|
57
|
+
```typescript
|
|
58
|
+
@controller('user-data')
|
|
59
|
+
class UserDataController implements IController<HTMLElement> {
|
|
60
|
+
element: HTMLElement | null = null;
|
|
61
|
+
private ctx?: Context;
|
|
62
|
+
|
|
63
|
+
attach(element: HTMLElement) { this.element = element; }
|
|
64
|
+
detach() { this.ctx = undefined; }
|
|
65
|
+
|
|
66
|
+
@context()
|
|
67
|
+
receiveContext(ctx: Context) {
|
|
68
|
+
this.ctx = ctx;
|
|
69
|
+
void this.load();
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
async load() {
|
|
73
|
+
const response = await this.ctx!.fetch('/api/users');
|
|
74
|
+
// Commit through the host's public API.
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
48
79
|
## Middleware types
|
|
49
80
|
|
|
50
81
|
### Request middleware — runs before the `fetch()` call
|
package/docs/ai/lifecycle.md
CHANGED
|
@@ -120,7 +120,10 @@ Options object always comes after the property names — works with multiple wat
|
|
|
120
120
|
|
|
121
121
|
## @context()
|
|
122
122
|
|
|
123
|
-
Receives router context updates
|
|
123
|
+
Receives router context updates on pages, descendant elements, and attached
|
|
124
|
+
controllers. Called whenever the router context changes (navigation, app
|
|
125
|
+
context update, etc.). A controller's managed decorators activate after
|
|
126
|
+
`attach()`, so context-dependent startup belongs in this handler:
|
|
124
127
|
|
|
125
128
|
```typescript
|
|
126
129
|
@property({ type: Array }) placards: Placard[] = [];
|
package/docs/ai/patterns.md
CHANGED
|
@@ -170,17 +170,17 @@ import { page } from '../router'; // NOT from 'snice'!
|
|
|
170
170
|
@page({ tag: 'user-page', routes: ['/users/:id'], guards: [isAuthenticated] })
|
|
171
171
|
class UserPage extends HTMLElement {
|
|
172
172
|
@property() id = '';
|
|
173
|
-
private
|
|
173
|
+
private ctx?: Context;
|
|
174
174
|
|
|
175
175
|
@context()
|
|
176
176
|
handleContext(ctx: Context) {
|
|
177
|
-
this.
|
|
177
|
+
this.ctx = ctx;
|
|
178
178
|
this.requestRender();
|
|
179
179
|
}
|
|
180
180
|
|
|
181
181
|
@ready()
|
|
182
182
|
async load() {
|
|
183
|
-
const user = await fetch(`/api/users/${this.id}`).then(r => r.json());
|
|
183
|
+
const user = await this.ctx!.fetch(`/api/users/${this.id}`).then(r => r.json());
|
|
184
184
|
// ...
|
|
185
185
|
}
|
|
186
186
|
}
|
|
@@ -219,9 +219,14 @@ Mock controller for tests, real API controller in production — same element.
|
|
|
219
219
|
// Controller responds to requests (receives payload, returns result directly)
|
|
220
220
|
@controller('api')
|
|
221
221
|
class API {
|
|
222
|
+
private ctx!: Context;
|
|
223
|
+
|
|
224
|
+
@context()
|
|
225
|
+
receiveContext(ctx: Context) { this.ctx = ctx; }
|
|
226
|
+
|
|
222
227
|
@respond('fetch-user')
|
|
223
228
|
async handleFetchUser(payload: { id: string }) {
|
|
224
|
-
const user = await fetch(`/api/users/${payload.id}`).then(r => r.json());
|
|
229
|
+
const user = await this.ctx.fetch(`/api/users/${payload.id}`).then(r => r.json());
|
|
225
230
|
return user; // Direct return, not callback
|
|
226
231
|
}
|
|
227
232
|
}
|