snice 7.6.0 → 7.8.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/adapters/react/SniceProvider.js +1 -1
- package/adapters/react/SniceRouter.js +1 -1
- package/adapters/react/calendar.d.ts +1 -0
- package/adapters/react/calendar.d.ts.map +1 -1
- package/adapters/react/calendar.js +1 -1
- package/adapters/react/calendar.js.map +1 -1
- package/adapters/react/calendar.tsx +2 -1
- package/adapters/react/matchRoute.js +1 -1
- package/adapters/react/useRequestHandler.js +1 -1
- package/bin/analyzer-contracts.js +8 -2
- package/custom-elements.json +15 -1
- 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 +2 -2
- package/dist/cdn/button/snice-button.js.map +1 -1
- package/dist/cdn/button/snice-button.min.js +1 -1
- package/dist/cdn/button/snice-button.min.js.map +1 -1
- package/dist/cdn/calendar/README.md +2 -2
- package/dist/cdn/calendar/snice-calendar.js +26 -2
- package/dist/cdn/calendar/snice-calendar.js.map +1 -1
- package/dist/cdn/calendar/snice-calendar.min.js +2 -2
- package/dist/cdn/calendar/snice-calendar.min.js.map +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/README.md +1 -1
- package/dist/cdn/login/snice-login.js +2 -2
- package/dist/cdn/login/snice-login.js.map +1 -1
- package/dist/cdn/login/snice-login.min.js +1 -1
- package/dist/cdn/login/snice-login.min.js.map +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 +130 -31
- 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 +130 -31
- 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/README.md +2 -2
- package/dist/cdn/table/snice-table.esm.js +227 -36
- package/dist/cdn/table/snice-table.esm.js.map +1 -1
- package/dist/cdn/table/snice-table.esm.min.js +69 -48
- package/dist/cdn/table/snice-table.esm.min.js.map +1 -1
- package/dist/cdn/table/snice-table.js +226 -35
- package/dist/cdn/table/snice-table.js.map +1 -1
- package/dist/cdn/table/snice-table.min.js +47 -26
- package/dist/cdn/table/snice-table.min.js.map +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/README.md +2 -2
- package/dist/cdn/tag-input/snice-tag-input.js +10 -16
- package/dist/cdn/tag-input/snice-tag-input.js.map +1 -1
- package/dist/cdn/tag-input/snice-tag-input.min.js +2 -2
- package/dist/cdn/tag-input/snice-tag-input.min.js.map +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/button/snice-button.js +1 -1
- package/dist/components/button/snice-button.js.map +1 -1
- package/dist/components/calendar/snice-calendar.d.ts +2 -0
- package/dist/components/calendar/snice-calendar.js +25 -1
- package/dist/components/calendar/snice-calendar.js.map +1 -1
- package/dist/components/table/snice-row.d.ts +6 -0
- package/dist/components/table/snice-row.js +11 -3
- package/dist/components/table/snice-row.js.map +1 -1
- package/dist/components/table/snice-table.d.ts +40 -3
- package/dist/components/table/snice-table.js +157 -17
- package/dist/components/table/snice-table.js.map +1 -1
- package/dist/components/table/table-cell-presentation.d.ts +11 -0
- package/dist/components/table/table-cell-presentation.js +25 -8
- package/dist/components/table/table-cell-presentation.js.map +1 -1
- package/dist/components/table/table-export.d.ts +6 -0
- package/dist/components/table/table-export.js +11 -2
- package/dist/components/table/table-export.js.map +1 -1
- package/dist/components/table/table-keyboard.js +1 -1
- package/dist/components/table/table-keyboard.js.map +1 -1
- package/dist/components/table/table-master-detail.d.ts +11 -2
- package/dist/components/table/table-master-detail.js +20 -3
- package/dist/components/table/table-master-detail.js.map +1 -1
- package/dist/components/tag-input/snice-tag-input.d.ts +2 -2
- package/dist/components/tag-input/snice-tag-input.js +9 -15
- package/dist/components/tag-input/snice-tag-input.js.map +1 -1
- package/dist/index.cjs +127 -28
- package/dist/index.cjs.map +1 -1
- package/dist/index.esm.js +127 -28
- package/dist/index.esm.js.map +1 -1
- package/dist/index.iife.js +127 -28
- 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/request-response.d.ts +24 -1
- package/dist/symbols.cjs +1 -1
- package/dist/symbols.esm.js +1 -1
- package/dist/testing-dom.cjs +1 -1
- package/dist/testing-dom.esm.js +1 -1
- package/dist/transitions.cjs +1 -1
- package/dist/transitions.esm.js +1 -1
- package/docs/ai/DEVELOPMENT.md +1 -1
- package/docs/ai/components/calendar.md +8 -0
- package/docs/ai/components/tag-input.md +4 -0
- package/docs/ai/controllers.md +22 -6
- package/docs/components/calendar.md +24 -0
- package/docs/components/tag-input.md +6 -1
- package/docs/controllers.md +57 -6
- package/package.json +1 -1
package/dist/index.esm.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* snice v7.
|
|
2
|
+
* snice v7.7.0
|
|
3
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
4
|
* (c) 2024
|
|
5
5
|
* Released under the MIT License.
|
|
@@ -1557,17 +1557,33 @@ function respond(requestName, options) {
|
|
|
1557
1557
|
});
|
|
1558
1558
|
};
|
|
1559
1559
|
}
|
|
1560
|
-
//
|
|
1561
|
-
|
|
1560
|
+
// Which handler descriptors an instance has already registered, so a second
|
|
1561
|
+
// setup pass only picks up what the first one deferred.
|
|
1562
|
+
const registeredHandlers = new WeakMap();
|
|
1563
|
+
/**
|
|
1564
|
+
* Helper to setup response handlers for elements and controllers.
|
|
1565
|
+
*
|
|
1566
|
+
* Safe to call more than once for the same instance: a handler already
|
|
1567
|
+
* registered is skipped rather than double-bound.
|
|
1568
|
+
*/
|
|
1569
|
+
function setupResponseHandlers(instance, element, options) {
|
|
1562
1570
|
const handlers = instance.constructor[CHANNEL_HANDLERS];
|
|
1563
1571
|
if (!handlers)
|
|
1564
1572
|
return;
|
|
1573
|
+
const claim = options?.claim;
|
|
1574
|
+
let registered = registeredHandlers.get(instance);
|
|
1575
|
+
if (!registered) {
|
|
1576
|
+
registered = new Set();
|
|
1577
|
+
registeredHandlers.set(instance, registered);
|
|
1578
|
+
}
|
|
1565
1579
|
// Store cleanup functions
|
|
1566
1580
|
// Initialize cleanup object if needed
|
|
1567
1581
|
if (!instance[CLEANUP]) {
|
|
1568
1582
|
instance[CLEANUP] = { events: [], channels: [] };
|
|
1569
1583
|
}
|
|
1570
1584
|
for (const handler of handlers) {
|
|
1585
|
+
if (registered.has(handler))
|
|
1586
|
+
continue;
|
|
1571
1587
|
const boundMethod = handler.method.bind(instance);
|
|
1572
1588
|
const eventName = `@request/${handler.channelName}`;
|
|
1573
1589
|
// Create timing variables for debounce/throttle per handler
|
|
@@ -1651,7 +1667,10 @@ function setupResponseHandlers(instance, element) {
|
|
|
1651
1667
|
// Clear the discovery timeout immediately - we found a handler
|
|
1652
1668
|
discovery.resolve();
|
|
1653
1669
|
// Call the timed responder method and handle the result
|
|
1654
|
-
|
|
1670
|
+
const answer = claim
|
|
1671
|
+
? Promise.resolve(claim()).then(() => timedMethod(payload))
|
|
1672
|
+
: Promise.resolve(timedMethod(payload));
|
|
1673
|
+
answer
|
|
1655
1674
|
.then(result => {
|
|
1656
1675
|
data.resolve(result);
|
|
1657
1676
|
})
|
|
@@ -1666,10 +1685,17 @@ function setupResponseHandlers(instance, element) {
|
|
|
1666
1685
|
responseTarget = requireDaemonTarget(instance, handler.options.daemon);
|
|
1667
1686
|
}
|
|
1668
1687
|
catch (error) {
|
|
1688
|
+
// The app context reaches a controller through its host element, so it
|
|
1689
|
+
// can be unreachable while the host is still disconnected. Leave the
|
|
1690
|
+
// handler for the later pass instead of dropping it permanently.
|
|
1691
|
+
if (options?.deferUnresolvableDaemons)
|
|
1692
|
+
continue;
|
|
1669
1693
|
console.warn(`[snice/@respond] ${error.message} Responder "${handler.channelName}" skipped.`);
|
|
1694
|
+
registered.add(handler);
|
|
1670
1695
|
continue;
|
|
1671
1696
|
}
|
|
1672
1697
|
}
|
|
1698
|
+
registered.add(handler);
|
|
1673
1699
|
responseTarget.addEventListener(eventName, responseHandler);
|
|
1674
1700
|
instance[CLEANUP].channels.push(() => {
|
|
1675
1701
|
responseTarget.removeEventListener(eventName, responseHandler);
|
|
@@ -1678,6 +1704,7 @@ function setupResponseHandlers(instance, element) {
|
|
|
1678
1704
|
}
|
|
1679
1705
|
// Helper to cleanup response handlers
|
|
1680
1706
|
function cleanupResponseHandlers(instance) {
|
|
1707
|
+
registeredHandlers.delete(instance);
|
|
1681
1708
|
if (instance[CLEANUP]?.channels) {
|
|
1682
1709
|
for (const cleanup of instance[CLEANUP].channels) {
|
|
1683
1710
|
cleanup();
|
|
@@ -1803,6 +1830,8 @@ function matchesKeyboardFilter(event, filter) {
|
|
|
1803
1830
|
* Use in elements or controllers to listen to DOM events or custom events
|
|
1804
1831
|
*/
|
|
1805
1832
|
const ON_HANDLERS = getSymbol('on-handlers');
|
|
1833
|
+
// Per-decoration-site counter — see the registration identity note in `on()`.
|
|
1834
|
+
let registrationCount = 0;
|
|
1806
1835
|
/**
|
|
1807
1836
|
* @on decorator for listening to events
|
|
1808
1837
|
*
|
|
@@ -1864,18 +1893,42 @@ function on(eventName, selectorOrOptions, options) {
|
|
|
1864
1893
|
if (!selector && opts.target) {
|
|
1865
1894
|
selector = opts.target;
|
|
1866
1895
|
}
|
|
1896
|
+
const eventNames = Array.isArray(eventName) ? eventName : [eventName];
|
|
1897
|
+
// Registration identity — derived from EVERY decorator argument, so no
|
|
1898
|
+
// option can be forgotten here and silently collapse two distinct stacked
|
|
1899
|
+
// @on decorators into one (that is how { debounce: 300 } stacked on a plain
|
|
1900
|
+
// @on for the same event used to lose one of its two registrations).
|
|
1901
|
+
// Options are serialized generically (sorted, so literal key order doesn't
|
|
1902
|
+
// matter); values that can't be compared by value — an EventTarget or a
|
|
1903
|
+
// resolver function passed as `scope` — fall back to this decoration site's
|
|
1904
|
+
// own id, which is stable across instances but unique per @on application.
|
|
1905
|
+
const siteId = `site:${++registrationCount}`;
|
|
1906
|
+
let optionSignature = '';
|
|
1907
|
+
let hasOpaqueOption = false;
|
|
1908
|
+
for (const key of Object.keys(opts).sort()) {
|
|
1909
|
+
const value = opts[key];
|
|
1910
|
+
if (value === undefined)
|
|
1911
|
+
continue;
|
|
1912
|
+
if (value !== null && (typeof value === 'object' || typeof value === 'function')) {
|
|
1913
|
+
hasOpaqueOption = true;
|
|
1914
|
+
continue;
|
|
1915
|
+
}
|
|
1916
|
+
optionSignature += `${key}=${String(value)};`;
|
|
1917
|
+
}
|
|
1918
|
+
const argumentKey = `${eventNames.join(',')}::${selector ?? ''}::${optionSignature}${hasOpaqueOption ? `::${siteId}` : ''}`;
|
|
1867
1919
|
return function (originalMethod, context) {
|
|
1868
1920
|
const methodName = context.name;
|
|
1921
|
+
// Same decoration, same method name → same registration. A subclass that
|
|
1922
|
+
// re-declares an identically decorated method must NOT add a second
|
|
1923
|
+
// listener on top of the one its parent's initializer already registered.
|
|
1924
|
+
const registrationKey = `${methodName}::${argumentKey}`;
|
|
1869
1925
|
context.addInitializer(function () {
|
|
1870
1926
|
const constructor = this.constructor;
|
|
1871
|
-
// Dedup by registration identity
|
|
1872
|
-
//
|
|
1873
|
-
//
|
|
1874
|
-
//
|
|
1875
|
-
//
|
|
1876
|
-
// prototype chain.
|
|
1877
|
-
const eventNames = Array.isArray(eventName) ? eventName : [eventName];
|
|
1878
|
-
const registrationKey = `${methodName}::${eventNames.join(',')}::${selector ?? ''}::daemon:${opts.daemon ?? ''}::light:${opts.light ?? ''}::shadow:${opts.shadow ?? ''}`;
|
|
1927
|
+
// Dedup by registration identity, NOT by method reference — stacked @on
|
|
1928
|
+
// decorators share one method and must each register. Use hasOwnProperty
|
|
1929
|
+
// so subclasses get their OWN Set instead of inheriting — otherwise
|
|
1930
|
+
// parent and child share state and child registrations pollute parent
|
|
1931
|
+
// (and vice versa) via the prototype chain.
|
|
1879
1932
|
if (!Object.prototype.hasOwnProperty.call(constructor, ON_METHODS)) {
|
|
1880
1933
|
constructor[ON_METHODS] = new Set();
|
|
1881
1934
|
}
|
|
@@ -2890,6 +2943,35 @@ async function attachController(element, controller) {
|
|
|
2890
2943
|
// MutationObservers are guarded from treating it as a string attachment.
|
|
2891
2944
|
restoreDirectControllerAttribute(element);
|
|
2892
2945
|
}
|
|
2946
|
+
// @respond handlers are installed before the readiness gate below, exactly
|
|
2947
|
+
// as elements install theirs in connectedCallback. A child's mount-time
|
|
2948
|
+
// @request can be the very thing keeping the host from becoming ready, so
|
|
2949
|
+
// the responder has to be reachable while `ready` is still pending. Such a
|
|
2950
|
+
// request also releases the gate — it proves the host subtree is live and
|
|
2951
|
+
// that something is blocked on this attachment — and is answered once
|
|
2952
|
+
// attach() and the remaining setup have run.
|
|
2953
|
+
//
|
|
2954
|
+
// A `@respond({ daemon })` handler resolves its target through the host's app
|
|
2955
|
+
// context, which is unreachable while the host is still disconnected (attach
|
|
2956
|
+
// before appendChild). Those are deferred here and registered by the second
|
|
2957
|
+
// pass past the gate, where they behaved correctly before this ordering.
|
|
2958
|
+
let requestClaimed = false;
|
|
2959
|
+
let releaseReadyGate;
|
|
2960
|
+
let completeAttach;
|
|
2961
|
+
let failAttach;
|
|
2962
|
+
const attached = new Promise((resolve, reject) => {
|
|
2963
|
+
completeAttach = resolve;
|
|
2964
|
+
failAttach = reject;
|
|
2965
|
+
});
|
|
2966
|
+
attached.catch(() => { }); // No requester when nothing was claimed
|
|
2967
|
+
setupResponseHandlers(controllerInstance, element, {
|
|
2968
|
+
deferUnresolvableDaemons: true,
|
|
2969
|
+
claim: () => {
|
|
2970
|
+
requestClaimed = true;
|
|
2971
|
+
releaseReadyGate?.();
|
|
2972
|
+
return attached;
|
|
2973
|
+
},
|
|
2974
|
+
});
|
|
2893
2975
|
// Normally a controller waits for its host's full initialization. The sole
|
|
2894
2976
|
// exception is attachController(host, ...) from that host's own @ready
|
|
2895
2977
|
// handler: awaiting ready there would wait on the current handler itself.
|
|
@@ -2902,6 +2984,11 @@ async function attachController(element, controller) {
|
|
|
2902
2984
|
try {
|
|
2903
2985
|
await Promise.race([
|
|
2904
2986
|
element.ready,
|
|
2987
|
+
new Promise(resolve => {
|
|
2988
|
+
if (requestClaimed)
|
|
2989
|
+
return resolve();
|
|
2990
|
+
releaseReadyGate = resolve;
|
|
2991
|
+
}),
|
|
2905
2992
|
new Promise((_, reject) => {
|
|
2906
2993
|
const onAbort = () => {
|
|
2907
2994
|
// Tagged so fire-and-forget call sites can tell this designed
|
|
@@ -2920,8 +3007,10 @@ async function attachController(element, controller) {
|
|
|
2920
3007
|
}
|
|
2921
3008
|
catch (error) {
|
|
2922
3009
|
// Attach never got past `ready` (aborted or deadline). Roll back the
|
|
2923
|
-
// stored references so a later attach does not
|
|
2924
|
-
// instance whose attach() never ran.
|
|
3010
|
+
// stored references and the early responders so a later attach does not
|
|
3011
|
+
// short-circuit against an instance whose attach() never ran.
|
|
3012
|
+
cleanupResponseHandlers(controllerInstance);
|
|
3013
|
+
failAttach(error);
|
|
2925
3014
|
if (element[CONTROLLER_KEY] === controllerInstance) {
|
|
2926
3015
|
if (typeof controller !== 'string') {
|
|
2927
3016
|
writeControllerAttribute(element, null);
|
|
@@ -2943,20 +3032,30 @@ async function attachController(element, controller) {
|
|
|
2943
3032
|
}
|
|
2944
3033
|
// Past `ready` — mark the controller so detach knows attach was actually run.
|
|
2945
3034
|
controllerInstance[CONTROLLER_ATTACHED] = true;
|
|
2946
|
-
|
|
2947
|
-
|
|
2948
|
-
await
|
|
2949
|
-
|
|
2950
|
-
|
|
2951
|
-
|
|
2952
|
-
|
|
2953
|
-
|
|
2954
|
-
|
|
2955
|
-
|
|
2956
|
-
|
|
2957
|
-
|
|
2958
|
-
|
|
2959
|
-
|
|
3035
|
+
try {
|
|
3036
|
+
// Run attach in the controller's scope
|
|
3037
|
+
await scope.runOperation(async () => {
|
|
3038
|
+
await controllerInstance.attach(element);
|
|
3039
|
+
});
|
|
3040
|
+
// @context has the same managed lifecycle on controllers as @on,
|
|
3041
|
+
// @observe and @respond. Catch up controllers whose async attach completed
|
|
3042
|
+
// after the Router announced the current navigation.
|
|
3043
|
+
await setupContextHandler(controllerInstance, 'microtask');
|
|
3044
|
+
// Setup @observe observers for controller
|
|
3045
|
+
setupObservers(controllerInstance, element);
|
|
3046
|
+
// Second @respond pass: registers only what the pre-gate pass deferred —
|
|
3047
|
+
// daemon-scoped responders whose app context was not resolvable yet.
|
|
3048
|
+
setupResponseHandlers(controllerInstance, element);
|
|
3049
|
+
// Setup @on event handlers for controller
|
|
3050
|
+
setupEventHandlers(controllerInstance, element);
|
|
3051
|
+
}
|
|
3052
|
+
catch (error) {
|
|
3053
|
+
// A claimed request must not wait on an attachment that never completed.
|
|
3054
|
+
failAttach(error);
|
|
3055
|
+
throw error;
|
|
3056
|
+
}
|
|
3057
|
+
// Answer whatever was claimed while the host was still becoming ready.
|
|
3058
|
+
completeAttach();
|
|
2960
3059
|
element.dispatchEvent(new CustomEvent('controller-attached', {
|
|
2961
3060
|
detail: { name: controllerName, controller: controllerInstance }
|
|
2962
3061
|
}));
|