@conduction/nextcloud-vue 2.66.0 → 2.68.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.
Files changed (95) hide show
  1. package/dist/esm/components/CnActionsBar/CnActionsBar.vue2.js +19 -0
  2. package/dist/esm/components/CnActionsBar/CnActionsBar.vue2.js.map +1 -1
  3. package/dist/esm/components/CnActionsBar/CnActionsBar.vue3.js +10 -3
  4. package/dist/esm/components/CnActionsBar/CnActionsBar.vue3.js.map +1 -1
  5. package/dist/esm/components/CnAppNav/CnAppNav.vue2.js +2 -2
  6. package/dist/esm/components/CnAppNav/CnAppNav.vue2.js.map +1 -1
  7. package/dist/esm/components/CnAppNav/CnAppNav.vue3.js.map +1 -1
  8. package/dist/esm/components/CnContextMenu/CnContextMenu.vue2.js +37 -26
  9. package/dist/esm/components/CnContextMenu/CnContextMenu.vue2.js.map +1 -1
  10. package/dist/esm/components/CnContextMenu/CnContextMenu.vue3.js +3 -3
  11. package/dist/esm/components/CnContextMenu/CnContextMenu.vue3.js.map +1 -1
  12. package/dist/esm/components/CnDataTable/CnDataTable.vue2.js +4 -4
  13. package/dist/esm/components/CnDataTable/CnDataTable.vue2.js.map +1 -1
  14. package/dist/esm/components/CnDataTable/CnDataTable.vue3.js.map +1 -1
  15. package/dist/esm/components/CnFormDialog/CnFormDialog.vue2.js +87 -18
  16. package/dist/esm/components/CnFormDialog/CnFormDialog.vue2.js.map +1 -1
  17. package/dist/esm/components/CnFormDialog/CnFormDialog.vue3.js +6 -1
  18. package/dist/esm/components/CnFormDialog/CnFormDialog.vue3.js.map +1 -1
  19. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +89 -49
  20. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
  21. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js +337 -315
  22. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
  23. package/dist/esm/components/CnIndexPage/defaultActions.js +6 -7
  24. package/dist/esm/components/CnIndexPage/defaultActions.js.map +1 -1
  25. package/dist/esm/components/CnRowActions/CnRowActions.vue2.js +33 -34
  26. package/dist/esm/components/CnRowActions/CnRowActions.vue2.js.map +1 -1
  27. package/dist/esm/components/CnRowActions/CnRowActions.vue3.js +3 -3
  28. package/dist/esm/components/CnRowActions/CnRowActions.vue3.js.map +1 -1
  29. package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue2.js +13 -13
  30. package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue2.js.map +1 -1
  31. package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue3.js.map +1 -1
  32. package/dist/esm/components/CnTimelineWidget/CnTimelineWidget.vue2.js +3 -2
  33. package/dist/esm/components/CnTimelineWidget/CnTimelineWidget.vue2.js.map +1 -1
  34. package/dist/esm/components/CnTimelineWidget/CnTimelineWidget.vue3.js.map +1 -1
  35. package/dist/esm/composables/useContextMenu.js +4 -2
  36. package/dist/esm/composables/useContextMenu.js.map +1 -1
  37. package/dist/esm/composables/useDependencyCheck.js +44 -1
  38. package/dist/esm/composables/useDependencyCheck.js.map +1 -1
  39. package/dist/esm/composables/useListView.js +43 -1
  40. package/dist/esm/composables/useListView.js.map +1 -1
  41. package/dist/esm/composables/useSetupStatus.js +26 -3
  42. package/dist/esm/composables/useSetupStatus.js.map +1 -1
  43. package/dist/esm/l10n/en.json.js +4 -1
  44. package/dist/esm/l10n/en.json.js.map +1 -1
  45. package/dist/esm/l10n/nl.json.js +4 -1
  46. package/dist/esm/l10n/nl.json.js.map +1 -1
  47. package/dist/esm/nextcloud-vue.css +20 -2
  48. package/dist/esm/store/plugins/liveUpdates.js +19 -9
  49. package/dist/esm/store/plugins/liveUpdates.js.map +1 -1
  50. package/dist/esm/store/useObjectStore.js +20 -9
  51. package/dist/esm/store/useObjectStore.js.map +1 -1
  52. package/dist/esm/utils/resolveRowActions.js +130 -0
  53. package/dist/esm/utils/resolveRowActions.js.map +1 -0
  54. package/dist/esm/utils/rowActionItem.js +104 -0
  55. package/dist/esm/utils/rowActionItem.js.map +1 -0
  56. package/dist/esm/utils/timelineEvents.js +44 -1
  57. package/dist/esm/utils/timelineEvents.js.map +1 -1
  58. package/dist/esm/utils/validateManifest.js +59 -3
  59. package/dist/esm/utils/validateManifest.js.map +1 -1
  60. package/dist/esm/utils/validateManifestV2.compiled.js +2 -2
  61. package/dist/esm/utils/validateManifestV2.compiled.js.map +1 -1
  62. package/dist/nextcloud-vue.cjs.js +1431 -802
  63. package/dist/nextcloud-vue.cjs.js.map +2 -2
  64. package/dist/nextcloud-vue.css +20 -2
  65. package/l10n/en.json +4 -1
  66. package/l10n/nl.json +4 -1
  67. package/package.json +1 -1
  68. package/scripts/.jsdoc-baselines.json +2 -2
  69. package/src/components/CnActionsBar/CnActionsBar.md +2 -1
  70. package/src/components/CnActionsBar/CnActionsBar.vue +25 -1
  71. package/src/components/CnAppNav/CnAppNav.vue +2 -2
  72. package/src/components/CnContextMenu/CnContextMenu.vue +40 -29
  73. package/src/components/CnDataTable/CnDataTable.md +1 -1
  74. package/src/components/CnDataTable/CnDataTable.vue +4 -4
  75. package/src/components/CnFormDialog/CnFormDialog.vue +92 -18
  76. package/src/components/CnIndexPage/CnIndexPage.md +23 -1
  77. package/src/components/CnIndexPage/CnIndexPage.vue +108 -52
  78. package/src/components/CnIndexPage/defaultActions.js +6 -7
  79. package/src/components/CnRowActions/CnRowActions.vue +36 -37
  80. package/src/components/CnSetupWizard/CnSetupWizard.vue +13 -13
  81. package/src/components/CnTimelineWidget/CnTimelineWidget.vue +3 -2
  82. package/src/composables/useContextMenu.js +4 -2
  83. package/src/composables/useDependencyCheck.js +43 -0
  84. package/src/composables/useListView.js +43 -1
  85. package/src/composables/useSetupStatus.js +26 -3
  86. package/src/css/actions-bar.css +20 -2
  87. package/src/schemas/app-manifest-v2.schema.json +48 -10
  88. package/src/schemas/app-manifest.schema.json +1 -1
  89. package/src/store/plugins/liveUpdates.js +19 -9
  90. package/src/store/useObjectStore.js +19 -8
  91. package/src/utils/resolveRowActions.js +127 -0
  92. package/src/utils/rowActionItem.js +100 -0
  93. package/src/utils/timelineEvents.js +43 -0
  94. package/src/utils/validateManifest.js +60 -3
  95. package/src/utils/validateManifestV2.compiled.js +2 -2
@@ -423,19 +423,29 @@ function liveUpdatesPlugin(opts = {}) {
423
423
  const collectionInFlight = new Map();
424
424
 
425
425
  const originalFetchCollection = store.fetchCollection.bind(store);
426
- store.fetchCollection = async function dedupedFetchCollection(type, params = {}) {
426
+ store.fetchCollection = async function dedupedFetchCollection(type, params = {}, options = {}) {
427
427
  if (!store.__liveDedupActive) {
428
- return originalFetchCollection(type, params)
428
+ return originalFetchCollection(type, params, options)
429
429
  }
430
430
  const key = collectionDedupKey(type, params);
431
- if (collectionInFlight.has(key)) {
432
- return collectionInFlight.get(key)
431
+ let entry = collectionInFlight.get(key);
432
+ if (!entry) {
433
+ // The shared request reports into its own sink, copied to
434
+ // each caller's `options.outcome` below, so a caller that
435
+ // joined it learns that request's result too.
436
+ const outcome = {};
437
+ const promise = originalFetchCollection(type, params, { ...options, outcome }).finally(() => {
438
+ collectionInFlight.delete(key);
439
+ });
440
+ entry = { promise, outcome };
441
+ collectionInFlight.set(key, entry);
433
442
  }
434
- const promise = originalFetchCollection(type, params).finally(() => {
435
- collectionInFlight.delete(key);
436
- });
437
- collectionInFlight.set(key, promise);
438
- return promise
443
+ const results = await entry.promise;
444
+ const sink = options && options.outcome;
445
+ if (sink && typeof sink === 'object' && 'error' in entry.outcome) {
446
+ sink.error = entry.outcome.error;
447
+ }
448
+ return results
439
449
  };
440
450
  },
441
451
  }
@@ -1 +1 @@
1
- {"version":3,"file":"liveUpdates.js","sources":["../../../../src/store/plugins/liveUpdates.js"],"sourcesContent":["/**\n * liveUpdatesPlugin — Pinia plugin factory for OpenRegister real-time updates.\n *\n * Adds live-update subscription support to any store created via createObjectStore().\n * Uses @nextcloud/notify_push via the transport singleton, with automatic polling\n * fallback when push is unavailable.\n *\n * Contributed state:\n * - `liveStatus` — current transport status string (`'offline'|'connecting'|'live'|'reconnecting'|'polling'`)\n * - `liveSubscriptions` — number of active subscriptions\n * - `liveLastEventAt` — Date of last received event, or null\n *\n * Contributed getters:\n * - `getLiveStatus`, `getLiveSubscriptions`, `getLiveLastEventAt`\n *\n * Contributed actions:\n * - `subscribe(type, id?, opts?)` — subscribe to live updates; returns a handle\n * - `unsubscribe(handle)` — tear down a subscription\n *\n * Usage note (Options API):\n * `tryOnScopeDispose` auto-registers cleanup when called from a Vue setup()\n * or Options API mounted() with an active effect scope. In Options API mounted()\n * without Vue 2.7 scope tracking, manual `unsubscribe(handle)` in beforeDestroy\n * is required. Document this at the call site.\n *\n * In-flight dedup:\n * Concurrent calls to fetchObject(type, id) or fetchCollection(type, params)\n * for the same key are coalesced into one HTTP request. The dedup maps live as\n * plain (non-reactive) Maps on the store instance to avoid Vue 2 overhead.\n * Dedup activates on the FIRST `subscribe()` call — before that, fetch actions\n * pass straight through to the base implementations. This keeps the plugin\n * fully inert (zero behaviour change) on stores that never subscribe, which\n * matters now that createObjectStore installs this plugin by default\n * (live-updates-default-on).\n *\n * Laziness guarantee:\n * Installing the plugin causes NO transport activity. The transport singleton\n * (and therefore the notify_push `listen()` probe / websocket connection /\n * polling timers) is only created inside the `subscribe()` action.\n *\n * Event coalescing:\n * Live events are HINTS (\"something changed\"), not payloads — each one\n * triggers a refetch. A burst of events (mass import, bulk save, another\n * user's rapid edits) would fire a refetch per event, so the dispatch is\n * coalesced per subscription: the FIRST event refetches immediately, and\n * any further events inside the `refetchDebounce` window (default 750 ms)\n * collapse into ONE trailing refetch when the window closes. Pending\n * trailing refetches are cancelled by `unsubscribe()`.\n *\n * @param {object} [opts] Plugin options\n * @param {number} [opts.pollIntervalCollection=30000] Collection poll interval (ms)\n * @param {number} [opts.pollIntervalObject=60000] Object poll interval (ms)\n * @param {number} [opts.refetchDebounce=750] Event-burst coalescing window (ms); 0 disables\n * @return {object} Plugin definition for use with createObjectStore\n *\n * @example\n * import { createObjectStore, liveUpdatesPlugin } from '@conduction/nextcloud-vue'\n *\n * const useStore = createObjectStore('myStore', {\n * plugins: [liveUpdatesPlugin({ pollIntervalCollection: 15000 })],\n * })\n *\n * // In a Vue component:\n * const store = useStore()\n * const handle = await store.subscribe('melding') // collection\n * const handle2 = await store.subscribe('melding', id) // object\n * // In beforeDestroy (Options API without scope):\n * store.unsubscribe(handle)\n */\n\n// `tryOnScopeDispose` comes from @vueuse/core, a DECLARED (non-optional) peer\n// dependency, so it is imported statically.\n//\n// It used to be pulled in through a CommonJS call inside a try/catch \"to avoid\n// a hard failure if @vueuse/core is absent\". That could not work: this module\n// ships to `dist/esm/**` where that function is not defined, and @vueuse/core's\n// own `exports` map resolves \".\" to a single ESM file with no CommonJS\n// condition. The catch therefore fired every time, `_tryOnScopeDispose` was\n// permanently null, and auto-cleanup on scope dispose silently never happened —\n// subscriptions leaked with no warning anywhere.\nimport { tryOnScopeDispose as _tryOnScopeDispose } from '@vueuse/core'\nimport { buildCollectionKey, buildObjectKey } from '../liveUpdates/eventKeys.js'\nimport { getLiveUpdates } from '../liveUpdates/transport.js'\n\n/**\n * Compute a stable cache key for dedup of fetchCollection calls.\n *\n * @param {string} type Object type slug\n * @param {object} params Query params\n * @return {string}\n */\nfunction collectionDedupKey(type, params) {\n\treturn `${type}:${JSON.stringify(params)}`\n}\n\n/**\n * Compute the cache key for dedup of fetchObject calls.\n *\n * @param {string} type Object type slug\n * @param {string} id Object ID\n * @return {string}\n */\nfunction objectDedupKey(type, id) {\n\treturn `${type}:${id}`\n}\n\n/**\n * Wrap a refetch dispatcher in a leading+trailing burst coalescer.\n *\n * Events are hints, so the exact invocation count doesn't matter — only\n * that the data ends up fresh. The first `run()` dispatches immediately\n * (snappy single-event case); further `run()`s inside the `waitMs` window\n * collapse into one trailing dispatch when the window closes (which opens\n * a new window, so a sustained burst dispatches at most once per window).\n * `cancel()` clears any pending trailing dispatch — called on\n * unsubscribe so a torn-down subscription never fires a late refetch.\n *\n * @param {() => void} fn The refetch dispatcher.\n * @param {number} waitMs Coalescing window in ms; `<= 0` disables (every run dispatches).\n * @return {{run: () => void, cancel: () => void}} The coalesced dispatcher.\n */\nfunction createHintCoalescer(fn, waitMs) {\n\tlet timer = null\n\tlet pending = false\n\n\tfunction run() {\n\t\tif (!(waitMs > 0)) {\n\t\t\tfn()\n\t\t\treturn\n\t\t}\n\t\tif (timer) {\n\t\t\tpending = true\n\t\t\treturn\n\t\t}\n\t\tfn()\n\t\ttimer = setTimeout(() => {\n\t\t\ttimer = null\n\t\t\tif (pending) {\n\t\t\t\tpending = false\n\t\t\t\trun()\n\t\t\t}\n\t\t}, waitMs)\n\t}\n\n\tfunction cancel() {\n\t\tif (timer) {\n\t\t\tclearTimeout(timer)\n\t\t}\n\t\ttimer = null\n\t\tpending = false\n\t}\n\n\treturn { run, cancel }\n}\n\nexport function liveUpdatesPlugin(opts = {}) {\n\tconst pluginPollCollection = opts.pollIntervalCollection || 30000\n\tconst pluginPollObject = opts.pollIntervalObject || 60000\n\tconst pluginRefetchDebounce = opts.refetchDebounce === undefined ? 750 : opts.refetchDebounce\n\n\treturn {\n\t\tname: 'liveUpdates',\n\n\t\tstate: () => ({\n\t\t\t/** @type {'offline'|'connecting'|'live'|'reconnecting'|'polling'} */\n\t\t\tliveStatus: 'offline',\n\t\t\t/** @type {number} */\n\t\t\tliveSubscriptions: 0,\n\t\t\t/** @type {Date|null} */\n\t\t\tliveLastEventAt: null,\n\t\t}),\n\n\t\tgetters: {\n\t\t\t/**\n\t\t\t * Get the current transport status.\n\t\t\t *\n\t\t\t * @param {object} state Pinia state\n\t\t\t * @return {string}\n\t\t\t */\n\t\t\tgetLiveStatus: (state) => state.liveStatus,\n\n\t\t\t/**\n\t\t\t * Get the number of active subscriptions.\n\t\t\t *\n\t\t\t * @param {object} state Pinia state\n\t\t\t * @return {number}\n\t\t\t */\n\t\t\tgetLiveSubscriptions: (state) => state.liveSubscriptions,\n\n\t\t\t/**\n\t\t\t * Get the timestamp of the last received live event.\n\t\t\t *\n\t\t\t * @param {object} state Pinia state\n\t\t\t * @return {Date|null}\n\t\t\t */\n\t\t\tgetLiveLastEventAt: (state) => state.liveLastEventAt,\n\t\t},\n\n\t\tactions: {\n\t\t\t/**\n\t\t\t * Subscribe to live updates for an object type.\n\t\t\t *\n\t\t\t * When `id` is provided: subscribes to `or-object-{id}` (per-object events).\n\t\t\t * When `id` is omitted: subscribes to `or-collection-{registerSlug}-{schemaSlug}`\n\t\t\t * derived from objectTypeRegistry. Slugs are lazy-fetched if not already cached.\n\t\t\t *\n\t\t\t * **Options API note**: In `mounted()` without an active Vue scope,\n\t\t\t * `tryOnScopeDispose` cannot auto-register cleanup. Call `unsubscribe(handle)`\n\t\t\t * manually in `beforeDestroy`.\n\t\t\t *\n\t\t\t * @param {string} type Registered object type slug\n\t\t\t * @param {string} [id] Object UUID (omit for collection subscription)\n\t\t\t * @param {object} [subscribeOpts] Options\n\t\t\t * @param {number} [subscribeOpts.interval] Override poll interval (ms)\n\t\t\t * @return {Promise<object>} Opaque handle for unsubscribe()\n\t\t\t * @throws {Error} If type is not registered, or if lazy slug fetch fails (collection form)\n\t\t\t */\n\t\t\tasync subscribe(type, id, subscribeOpts = {}) {\n\t\t\t\tconst config = this.objectTypeRegistry[type]\n\t\t\t\tif (!config) {\n\t\t\t\t\tthrow new Error(`\"${type}\" is not registered. Call registerObjectType('${type}', ...) first.`)\n\t\t\t\t}\n\n\t\t\t\t// First subscribe() activates the in-flight dedup wrappers set\n\t\t\t\t// up in the plugin's setup() hook. Before this point the store\n\t\t\t\t// behaves exactly as if the plugin were not installed.\n\t\t\t\tthis.__liveDedupActive = true\n\n\t\t\t\tconst liveUpdates = getLiveUpdates({\n\t\t\t\t\tpollIntervalCollection: pluginPollCollection,\n\t\t\t\t\tpollIntervalObject: pluginPollObject,\n\t\t\t\t})\n\n\t\t\t\t// Register transport status observer (idempotent: only once per store instance)\n\t\t\t\tif (!this.__liveStatusObserverRegistered) {\n\t\t\t\t\tthis.__liveStatusObserverRegistered = true\n\t\t\t\t\tliveUpdates.onStatusChange((newStatus) => {\n\t\t\t\t\t\tthis.liveStatus = { ...this.liveStatus, valueOf: undefined, toString: undefined }\n\t\t\t\t\t\t// Vue 2 spread pattern for primitive reactive update\n\t\t\t\t\t\tthis.liveStatus = newStatus\n\t\t\t\t\t})\n\t\t\t\t}\n\n\t\t\t\tlet eventKey\n\t\t\t\tlet isObject = false\n\n\t\t\t\tif (id !== undefined && id !== null) {\n\t\t\t\t\t// Object subscription — no slug lookup needed\n\t\t\t\t\tisObject = true\n\t\t\t\t\teventKey = buildObjectKey(id)\n\t\t\t\t} else {\n\t\t\t\t\t// Collection subscription — need registerSlug + schemaSlug\n\t\t\t\t\tlet { registerSlug, schemaSlug } = config\n\n\t\t\t\t\tif (!registerSlug || !schemaSlug) {\n\t\t\t\t\t\t// Lazy fetch slugs via existing store actions\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tconst [register, schema] = await Promise.all([\n\t\t\t\t\t\t\t\tthis.fetchRegister(type),\n\t\t\t\t\t\t\t\tthis.fetchSchema(type),\n\t\t\t\t\t\t\t])\n\n\t\t\t\t\t\t\tif (!register || !register.slug) {\n\t\t\t\t\t\t\t\tthrow new Error(`Failed to resolve register slug for \"${type}\"`)\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (!schema || !schema.slug) {\n\t\t\t\t\t\t\t\tthrow new Error(`Failed to resolve schema slug for \"${type}\"`)\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tregisterSlug = register.slug\n\t\t\t\t\t\t\tschemaSlug = schema.slug\n\n\t\t\t\t\t\t\t// Cache slugs in registry (Vue 2 spread pattern)\n\t\t\t\t\t\t\tthis.objectTypeRegistry = {\n\t\t\t\t\t\t\t\t...this.objectTypeRegistry,\n\t\t\t\t\t\t\t\t[type]: { ...config, registerSlug, schemaSlug },\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} catch (err) {\n\t\t\t\t\t\t\tthrow new Error(`liveUpdatesPlugin: cannot subscribe to \"${type}\" collection — ${err.message}`, { cause: err })\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\teventKey = buildCollectionKey(registerSlug, schemaSlug)\n\t\t\t\t}\n\n\t\t\t\t// Set status to 'connecting' on first subscribe (before transport sets it)\n\t\t\t\tif (this.liveStatus === 'offline') {\n\t\t\t\t\tthis.liveStatus = 'connecting'\n\t\t\t\t}\n\n\t\t\t\tconst store = this\n\n\t\t\t\t// Events are hints — coalesce refetch bursts per subscription\n\t\t\t\t// (see \"Event coalescing\" in the module docblock). A per-call\n\t\t\t\t// `subscribeOpts.debounce` overrides the plugin default; 0 disables.\n\t\t\t\tconst debounceMs = subscribeOpts.debounce === undefined ? pluginRefetchDebounce : subscribeOpts.debounce\n\t\t\t\tconst dispatch = isObject\n\t\t\t\t\t? () => {\n\t\t\t\t\t\t// Dispatch fetchObject with dedup\n\t\t\t\t\t\t\tstore.fetchObject(type, id)\n\t\t\t\t\t\t}\n\t\t\t\t\t: () => {\n\t\t\t\t\t\t// Dispatch fetchCollection with last stashed params + dedup\n\t\t\t\t\t\t\tconst lastParams = store.__lastCollectionParams?.get(type) || {}\n\t\t\t\t\t\t\tstore.fetchCollection(type, lastParams)\n\t\t\t\t\t\t}\n\t\t\t\tconst coalesced = createHintCoalescer(dispatch, debounceMs)\n\n\t\t\t\tconst callback = () => {\n\t\t\t\t\tstore.liveLastEventAt = new Date()\n\t\t\t\t\tcoalesced.run()\n\t\t\t\t}\n\n\t\t\t\tconst transportOpts = {\n\t\t\t\t\tisObject,\n\t\t\t\t\tinterval: subscribeOpts.interval || (isObject ? pluginPollObject : pluginPollCollection),\n\t\t\t\t}\n\n\t\t\t\tconst transportHandle = liveUpdates.subscribe(eventKey, callback, transportOpts)\n\n\t\t\t\t// Update reactive subscriptions count (Vue 2 spread)\n\t\t\t\tthis.liveSubscriptions = this.liveSubscriptions + 1\n\n\t\t\t\t// Update liveStatus from transport now that we've subscribed\n\t\t\t\tconst transportStatus = liveUpdates.getStatus()\n\t\t\t\tif (transportStatus !== 'offline') {\n\t\t\t\t\tthis.liveStatus = transportStatus\n\t\t\t\t}\n\n\t\t\t\tconst handle = {\n\t\t\t\t\t_livePlugin: true,\n\t\t\t\t\ttype,\n\t\t\t\t\tid,\n\t\t\t\t\teventKey,\n\t\t\t\t\ttransportHandle,\n\t\t\t\t\t// Cancels a pending coalesced (trailing) refetch — called by\n\t\t\t\t\t// unsubscribe() so a torn-down subscription never fires late.\n\t\t\t\t\t_cancelPendingRefetch: coalesced.cancel,\n\t\t\t\t}\n\n\t\t\t\t// Auto-cleanup via tryOnScopeDispose (Vue 2.7 composition API scopes)\n\t\t\t\tif (_tryOnScopeDispose) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\t_tryOnScopeDispose(() => {\n\t\t\t\t\t\t\tthis.unsubscribe(handle)\n\t\t\t\t\t\t})\n\t\t\t\t\t} catch {\n\t\t\t\t\t\t// Not in a scope — manual unsubscribe required in beforeDestroy\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\treturn handle\n\t\t\t},\n\n\t\t\t/**\n\t\t\t * Unsubscribe from live updates. Idempotent — releasing the same\n\t\t\t * handle twice (e.g. the plugin's own scope-dispose cleanup racing\n\t\t\t * a composable's explicit detach) is a no-op the second time, so\n\t\t\t * `liveSubscriptions` never double-decrements.\n\t\t\t *\n\t\t\t * @param {object} handle Handle returned by subscribe()\n\t\t\t */\n\t\t\tunsubscribe(handle) {\n\t\t\t\tif (!handle || !handle._livePlugin || handle._released) {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\thandle._released = true\n\n\t\t\t\t// Cancel any pending coalesced refetch before tearing down the\n\t\t\t\t// transport subscription — a late trailing dispatch after\n\t\t\t\t// unsubscribe would refetch for a scope nobody renders anymore.\n\t\t\t\tif (typeof handle._cancelPendingRefetch === 'function') {\n\t\t\t\t\thandle._cancelPendingRefetch()\n\t\t\t\t}\n\n\t\t\t\tconst liveUpdates = getLiveUpdates()\n\t\t\t\tliveUpdates.unsubscribe(handle.transportHandle)\n\n\t\t\t\tthis.liveSubscriptions = Math.max(0, this.liveSubscriptions - 1)\n\t\t\t},\n\t\t},\n\n\t\t/**\n\t\t * Plugin setup hook — called once per store instance.\n\t\t * Sets up:\n\t\t * 1. `$onAction` observer for fetchCollection → stash last params\n\t\t * 2. In-flight dedup Maps for fetchObject and fetchCollection\n\t\t *\n\t\t * @param {object} store The Pinia store instance\n\t\t */\n\t\tsetup(store) {\n\t\t\t// Dedup activation flag — flipped to true by the first subscribe()\n\t\t\t// call. Until then the fetch wrappers below pass straight through,\n\t\t\t// so a default-installed plugin causes zero behaviour change on\n\t\t\t// stores that never subscribe (live-updates-default-on).\n\t\t\tstore.__liveDedupActive = false\n\n\t\t\t// -- Last-params stash for collection re-fetch on events --\n\t\t\t// Plain (non-reactive) Map: type → last params object\n\t\t\tstore.__lastCollectionParams = new Map()\n\n\t\t\tstore.$onAction(({ name, args, after }) => {\n\t\t\t\tif (name === 'fetchCollection') {\n\t\t\t\t\tconst [type, params = {}] = args\n\t\t\t\t\tafter(() => {\n\t\t\t\t\t\tstore.__lastCollectionParams.set(type, params)\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t})\n\n\t\t\t// -- In-flight dedup for fetchObject --\n\t\t\t// Plain Map: dedupKey → Promise\n\t\t\tconst objectInFlight = new Map()\n\n\t\t\tconst originalFetchObject = store.fetchObject.bind(store)\n\t\t\tstore.fetchObject = async function dedupedFetchObject(type, id) {\n\t\t\t\tif (!store.__liveDedupActive) {\n\t\t\t\t\treturn originalFetchObject(type, id)\n\t\t\t\t}\n\t\t\t\tconst key = objectDedupKey(type, id)\n\t\t\t\tif (objectInFlight.has(key)) {\n\t\t\t\t\treturn objectInFlight.get(key)\n\t\t\t\t}\n\t\t\t\tconst promise = originalFetchObject(type, id).finally(() => {\n\t\t\t\t\tobjectInFlight.delete(key)\n\t\t\t\t})\n\t\t\t\tobjectInFlight.set(key, promise)\n\t\t\t\treturn promise\n\t\t\t}\n\n\t\t\t// -- In-flight dedup for fetchCollection --\n\t\t\tconst collectionInFlight = new Map()\n\n\t\t\tconst originalFetchCollection = store.fetchCollection.bind(store)\n\t\t\tstore.fetchCollection = async function dedupedFetchCollection(type, params = {}) {\n\t\t\t\tif (!store.__liveDedupActive) {\n\t\t\t\t\treturn originalFetchCollection(type, params)\n\t\t\t\t}\n\t\t\t\tconst key = collectionDedupKey(type, params)\n\t\t\t\tif (collectionInFlight.has(key)) {\n\t\t\t\t\treturn collectionInFlight.get(key)\n\t\t\t\t}\n\t\t\t\tconst promise = originalFetchCollection(type, params).finally(() => {\n\t\t\t\t\tcollectionInFlight.delete(key)\n\t\t\t\t})\n\t\t\t\tcollectionInFlight.set(key, promise)\n\t\t\t\treturn promise\n\t\t\t}\n\t\t},\n\t}\n}\n"],"names":["_tryOnScopeDispose"],"mappings":";;;;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAcA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAAS,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE;AAC1C,CAAC,OAAO,CAAC,EAAE,IAAI,CAAC,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC;AAC3C,CAAC;AACD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAAS,cAAc,CAAC,IAAI,EAAE,EAAE,EAAE;AAClC,CAAC,OAAO,CAAC,EAAE,IAAI,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;AACvB,CAAC;AACD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAAS,mBAAmB,CAAC,EAAE,EAAE,MAAM,EAAE;AACzC,CAAC,IAAI,KAAK,GAAG,KAAI;AACjB,CAAC,IAAI,OAAO,GAAG,MAAK;AACpB;AACA,CAAC,SAAS,GAAG,GAAG;AAChB,EAAE,IAAI,EAAE,MAAM,GAAG,CAAC,CAAC,EAAE;AACrB,GAAG,EAAE,GAAE;AACP,GAAG,MAAM;AACT,GAAG;AACH,EAAE,IAAI,KAAK,EAAE;AACb,GAAG,OAAO,GAAG,KAAI;AACjB,GAAG,MAAM;AACT,GAAG;AACH,EAAE,EAAE,GAAE;AACN,EAAE,KAAK,GAAG,UAAU,CAAC,MAAM;AAC3B,GAAG,KAAK,GAAG,KAAI;AACf,GAAG,IAAI,OAAO,EAAE;AAChB,IAAI,OAAO,GAAG,MAAK;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"}
1
+ {"version":3,"file":"liveUpdates.js","sources":["../../../../src/store/plugins/liveUpdates.js"],"sourcesContent":["/**\n * liveUpdatesPlugin — Pinia plugin factory for OpenRegister real-time updates.\n *\n * Adds live-update subscription support to any store created via createObjectStore().\n * Uses @nextcloud/notify_push via the transport singleton, with automatic polling\n * fallback when push is unavailable.\n *\n * Contributed state:\n * - `liveStatus` — current transport status string (`'offline'|'connecting'|'live'|'reconnecting'|'polling'`)\n * - `liveSubscriptions` — number of active subscriptions\n * - `liveLastEventAt` — Date of last received event, or null\n *\n * Contributed getters:\n * - `getLiveStatus`, `getLiveSubscriptions`, `getLiveLastEventAt`\n *\n * Contributed actions:\n * - `subscribe(type, id?, opts?)` — subscribe to live updates; returns a handle\n * - `unsubscribe(handle)` — tear down a subscription\n *\n * Usage note (Options API):\n * `tryOnScopeDispose` auto-registers cleanup when called from a Vue setup()\n * or Options API mounted() with an active effect scope. In Options API mounted()\n * without Vue 2.7 scope tracking, manual `unsubscribe(handle)` in beforeDestroy\n * is required. Document this at the call site.\n *\n * In-flight dedup:\n * Concurrent calls to fetchObject(type, id) or fetchCollection(type, params)\n * for the same key are coalesced into one HTTP request. The dedup maps live as\n * plain (non-reactive) Maps on the store instance to avoid Vue 2 overhead.\n * Dedup activates on the FIRST `subscribe()` call — before that, fetch actions\n * pass straight through to the base implementations. This keeps the plugin\n * fully inert (zero behaviour change) on stores that never subscribe, which\n * matters now that createObjectStore installs this plugin by default\n * (live-updates-default-on).\n *\n * Laziness guarantee:\n * Installing the plugin causes NO transport activity. The transport singleton\n * (and therefore the notify_push `listen()` probe / websocket connection /\n * polling timers) is only created inside the `subscribe()` action.\n *\n * Event coalescing:\n * Live events are HINTS (\"something changed\"), not payloads — each one\n * triggers a refetch. A burst of events (mass import, bulk save, another\n * user's rapid edits) would fire a refetch per event, so the dispatch is\n * coalesced per subscription: the FIRST event refetches immediately, and\n * any further events inside the `refetchDebounce` window (default 750 ms)\n * collapse into ONE trailing refetch when the window closes. Pending\n * trailing refetches are cancelled by `unsubscribe()`.\n *\n * @param {object} [opts] Plugin options\n * @param {number} [opts.pollIntervalCollection=30000] Collection poll interval (ms)\n * @param {number} [opts.pollIntervalObject=60000] Object poll interval (ms)\n * @param {number} [opts.refetchDebounce=750] Event-burst coalescing window (ms); 0 disables\n * @return {object} Plugin definition for use with createObjectStore\n *\n * @example\n * import { createObjectStore, liveUpdatesPlugin } from '@conduction/nextcloud-vue'\n *\n * const useStore = createObjectStore('myStore', {\n * plugins: [liveUpdatesPlugin({ pollIntervalCollection: 15000 })],\n * })\n *\n * // In a Vue component:\n * const store = useStore()\n * const handle = await store.subscribe('melding') // collection\n * const handle2 = await store.subscribe('melding', id) // object\n * // In beforeDestroy (Options API without scope):\n * store.unsubscribe(handle)\n */\n\n// `tryOnScopeDispose` comes from @vueuse/core, a DECLARED (non-optional) peer\n// dependency, so it is imported statically.\n//\n// It used to be pulled in through a CommonJS call inside a try/catch \"to avoid\n// a hard failure if @vueuse/core is absent\". That could not work: this module\n// ships to `dist/esm/**` where that function is not defined, and @vueuse/core's\n// own `exports` map resolves \".\" to a single ESM file with no CommonJS\n// condition. The catch therefore fired every time, `_tryOnScopeDispose` was\n// permanently null, and auto-cleanup on scope dispose silently never happened —\n// subscriptions leaked with no warning anywhere.\nimport { tryOnScopeDispose as _tryOnScopeDispose } from '@vueuse/core'\nimport { buildCollectionKey, buildObjectKey } from '../liveUpdates/eventKeys.js'\nimport { getLiveUpdates } from '../liveUpdates/transport.js'\n\n/**\n * Compute a stable cache key for dedup of fetchCollection calls.\n *\n * @param {string} type Object type slug\n * @param {object} params Query params\n * @return {string}\n */\nfunction collectionDedupKey(type, params) {\n\treturn `${type}:${JSON.stringify(params)}`\n}\n\n/**\n * Compute the cache key for dedup of fetchObject calls.\n *\n * @param {string} type Object type slug\n * @param {string} id Object ID\n * @return {string}\n */\nfunction objectDedupKey(type, id) {\n\treturn `${type}:${id}`\n}\n\n/**\n * Wrap a refetch dispatcher in a leading+trailing burst coalescer.\n *\n * Events are hints, so the exact invocation count doesn't matter — only\n * that the data ends up fresh. The first `run()` dispatches immediately\n * (snappy single-event case); further `run()`s inside the `waitMs` window\n * collapse into one trailing dispatch when the window closes (which opens\n * a new window, so a sustained burst dispatches at most once per window).\n * `cancel()` clears any pending trailing dispatch — called on\n * unsubscribe so a torn-down subscription never fires a late refetch.\n *\n * @param {() => void} fn The refetch dispatcher.\n * @param {number} waitMs Coalescing window in ms; `<= 0` disables (every run dispatches).\n * @return {{run: () => void, cancel: () => void}} The coalesced dispatcher.\n */\nfunction createHintCoalescer(fn, waitMs) {\n\tlet timer = null\n\tlet pending = false\n\n\tfunction run() {\n\t\tif (!(waitMs > 0)) {\n\t\t\tfn()\n\t\t\treturn\n\t\t}\n\t\tif (timer) {\n\t\t\tpending = true\n\t\t\treturn\n\t\t}\n\t\tfn()\n\t\ttimer = setTimeout(() => {\n\t\t\ttimer = null\n\t\t\tif (pending) {\n\t\t\t\tpending = false\n\t\t\t\trun()\n\t\t\t}\n\t\t}, waitMs)\n\t}\n\n\tfunction cancel() {\n\t\tif (timer) {\n\t\t\tclearTimeout(timer)\n\t\t}\n\t\ttimer = null\n\t\tpending = false\n\t}\n\n\treturn { run, cancel }\n}\n\nexport function liveUpdatesPlugin(opts = {}) {\n\tconst pluginPollCollection = opts.pollIntervalCollection || 30000\n\tconst pluginPollObject = opts.pollIntervalObject || 60000\n\tconst pluginRefetchDebounce = opts.refetchDebounce === undefined ? 750 : opts.refetchDebounce\n\n\treturn {\n\t\tname: 'liveUpdates',\n\n\t\tstate: () => ({\n\t\t\t/** @type {'offline'|'connecting'|'live'|'reconnecting'|'polling'} */\n\t\t\tliveStatus: 'offline',\n\t\t\t/** @type {number} */\n\t\t\tliveSubscriptions: 0,\n\t\t\t/** @type {Date|null} */\n\t\t\tliveLastEventAt: null,\n\t\t}),\n\n\t\tgetters: {\n\t\t\t/**\n\t\t\t * Get the current transport status.\n\t\t\t *\n\t\t\t * @param {object} state Pinia state\n\t\t\t * @return {string}\n\t\t\t */\n\t\t\tgetLiveStatus: (state) => state.liveStatus,\n\n\t\t\t/**\n\t\t\t * Get the number of active subscriptions.\n\t\t\t *\n\t\t\t * @param {object} state Pinia state\n\t\t\t * @return {number}\n\t\t\t */\n\t\t\tgetLiveSubscriptions: (state) => state.liveSubscriptions,\n\n\t\t\t/**\n\t\t\t * Get the timestamp of the last received live event.\n\t\t\t *\n\t\t\t * @param {object} state Pinia state\n\t\t\t * @return {Date|null}\n\t\t\t */\n\t\t\tgetLiveLastEventAt: (state) => state.liveLastEventAt,\n\t\t},\n\n\t\tactions: {\n\t\t\t/**\n\t\t\t * Subscribe to live updates for an object type.\n\t\t\t *\n\t\t\t * When `id` is provided: subscribes to `or-object-{id}` (per-object events).\n\t\t\t * When `id` is omitted: subscribes to `or-collection-{registerSlug}-{schemaSlug}`\n\t\t\t * derived from objectTypeRegistry. Slugs are lazy-fetched if not already cached.\n\t\t\t *\n\t\t\t * **Options API note**: In `mounted()` without an active Vue scope,\n\t\t\t * `tryOnScopeDispose` cannot auto-register cleanup. Call `unsubscribe(handle)`\n\t\t\t * manually in `beforeDestroy`.\n\t\t\t *\n\t\t\t * @param {string} type Registered object type slug\n\t\t\t * @param {string} [id] Object UUID (omit for collection subscription)\n\t\t\t * @param {object} [subscribeOpts] Options\n\t\t\t * @param {number} [subscribeOpts.interval] Override poll interval (ms)\n\t\t\t * @return {Promise<object>} Opaque handle for unsubscribe()\n\t\t\t * @throws {Error} If type is not registered, or if lazy slug fetch fails (collection form)\n\t\t\t */\n\t\t\tasync subscribe(type, id, subscribeOpts = {}) {\n\t\t\t\tconst config = this.objectTypeRegistry[type]\n\t\t\t\tif (!config) {\n\t\t\t\t\tthrow new Error(`\"${type}\" is not registered. Call registerObjectType('${type}', ...) first.`)\n\t\t\t\t}\n\n\t\t\t\t// First subscribe() activates the in-flight dedup wrappers set\n\t\t\t\t// up in the plugin's setup() hook. Before this point the store\n\t\t\t\t// behaves exactly as if the plugin were not installed.\n\t\t\t\tthis.__liveDedupActive = true\n\n\t\t\t\tconst liveUpdates = getLiveUpdates({\n\t\t\t\t\tpollIntervalCollection: pluginPollCollection,\n\t\t\t\t\tpollIntervalObject: pluginPollObject,\n\t\t\t\t})\n\n\t\t\t\t// Register transport status observer (idempotent: only once per store instance)\n\t\t\t\tif (!this.__liveStatusObserverRegistered) {\n\t\t\t\t\tthis.__liveStatusObserverRegistered = true\n\t\t\t\t\tliveUpdates.onStatusChange((newStatus) => {\n\t\t\t\t\t\tthis.liveStatus = { ...this.liveStatus, valueOf: undefined, toString: undefined }\n\t\t\t\t\t\t// Vue 2 spread pattern for primitive reactive update\n\t\t\t\t\t\tthis.liveStatus = newStatus\n\t\t\t\t\t})\n\t\t\t\t}\n\n\t\t\t\tlet eventKey\n\t\t\t\tlet isObject = false\n\n\t\t\t\tif (id !== undefined && id !== null) {\n\t\t\t\t\t// Object subscription — no slug lookup needed\n\t\t\t\t\tisObject = true\n\t\t\t\t\teventKey = buildObjectKey(id)\n\t\t\t\t} else {\n\t\t\t\t\t// Collection subscription — need registerSlug + schemaSlug\n\t\t\t\t\tlet { registerSlug, schemaSlug } = config\n\n\t\t\t\t\tif (!registerSlug || !schemaSlug) {\n\t\t\t\t\t\t// Lazy fetch slugs via existing store actions\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tconst [register, schema] = await Promise.all([\n\t\t\t\t\t\t\t\tthis.fetchRegister(type),\n\t\t\t\t\t\t\t\tthis.fetchSchema(type),\n\t\t\t\t\t\t\t])\n\n\t\t\t\t\t\t\tif (!register || !register.slug) {\n\t\t\t\t\t\t\t\tthrow new Error(`Failed to resolve register slug for \"${type}\"`)\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (!schema || !schema.slug) {\n\t\t\t\t\t\t\t\tthrow new Error(`Failed to resolve schema slug for \"${type}\"`)\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tregisterSlug = register.slug\n\t\t\t\t\t\t\tschemaSlug = schema.slug\n\n\t\t\t\t\t\t\t// Cache slugs in registry (Vue 2 spread pattern)\n\t\t\t\t\t\t\tthis.objectTypeRegistry = {\n\t\t\t\t\t\t\t\t...this.objectTypeRegistry,\n\t\t\t\t\t\t\t\t[type]: { ...config, registerSlug, schemaSlug },\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} catch (err) {\n\t\t\t\t\t\t\tthrow new Error(`liveUpdatesPlugin: cannot subscribe to \"${type}\" collection — ${err.message}`, { cause: err })\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\teventKey = buildCollectionKey(registerSlug, schemaSlug)\n\t\t\t\t}\n\n\t\t\t\t// Set status to 'connecting' on first subscribe (before transport sets it)\n\t\t\t\tif (this.liveStatus === 'offline') {\n\t\t\t\t\tthis.liveStatus = 'connecting'\n\t\t\t\t}\n\n\t\t\t\tconst store = this\n\n\t\t\t\t// Events are hints — coalesce refetch bursts per subscription\n\t\t\t\t// (see \"Event coalescing\" in the module docblock). A per-call\n\t\t\t\t// `subscribeOpts.debounce` overrides the plugin default; 0 disables.\n\t\t\t\tconst debounceMs = subscribeOpts.debounce === undefined ? pluginRefetchDebounce : subscribeOpts.debounce\n\t\t\t\tconst dispatch = isObject\n\t\t\t\t\t? () => {\n\t\t\t\t\t\t// Dispatch fetchObject with dedup\n\t\t\t\t\t\t\tstore.fetchObject(type, id)\n\t\t\t\t\t\t}\n\t\t\t\t\t: () => {\n\t\t\t\t\t\t// Dispatch fetchCollection with last stashed params + dedup\n\t\t\t\t\t\t\tconst lastParams = store.__lastCollectionParams?.get(type) || {}\n\t\t\t\t\t\t\tstore.fetchCollection(type, lastParams)\n\t\t\t\t\t\t}\n\t\t\t\tconst coalesced = createHintCoalescer(dispatch, debounceMs)\n\n\t\t\t\tconst callback = () => {\n\t\t\t\t\tstore.liveLastEventAt = new Date()\n\t\t\t\t\tcoalesced.run()\n\t\t\t\t}\n\n\t\t\t\tconst transportOpts = {\n\t\t\t\t\tisObject,\n\t\t\t\t\tinterval: subscribeOpts.interval || (isObject ? pluginPollObject : pluginPollCollection),\n\t\t\t\t}\n\n\t\t\t\tconst transportHandle = liveUpdates.subscribe(eventKey, callback, transportOpts)\n\n\t\t\t\t// Update reactive subscriptions count (Vue 2 spread)\n\t\t\t\tthis.liveSubscriptions = this.liveSubscriptions + 1\n\n\t\t\t\t// Update liveStatus from transport now that we've subscribed\n\t\t\t\tconst transportStatus = liveUpdates.getStatus()\n\t\t\t\tif (transportStatus !== 'offline') {\n\t\t\t\t\tthis.liveStatus = transportStatus\n\t\t\t\t}\n\n\t\t\t\tconst handle = {\n\t\t\t\t\t_livePlugin: true,\n\t\t\t\t\ttype,\n\t\t\t\t\tid,\n\t\t\t\t\teventKey,\n\t\t\t\t\ttransportHandle,\n\t\t\t\t\t// Cancels a pending coalesced (trailing) refetch — called by\n\t\t\t\t\t// unsubscribe() so a torn-down subscription never fires late.\n\t\t\t\t\t_cancelPendingRefetch: coalesced.cancel,\n\t\t\t\t}\n\n\t\t\t\t// Auto-cleanup via tryOnScopeDispose (Vue 2.7 composition API scopes)\n\t\t\t\tif (_tryOnScopeDispose) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\t_tryOnScopeDispose(() => {\n\t\t\t\t\t\t\tthis.unsubscribe(handle)\n\t\t\t\t\t\t})\n\t\t\t\t\t} catch {\n\t\t\t\t\t\t// Not in a scope — manual unsubscribe required in beforeDestroy\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\treturn handle\n\t\t\t},\n\n\t\t\t/**\n\t\t\t * Unsubscribe from live updates. Idempotent — releasing the same\n\t\t\t * handle twice (e.g. the plugin's own scope-dispose cleanup racing\n\t\t\t * a composable's explicit detach) is a no-op the second time, so\n\t\t\t * `liveSubscriptions` never double-decrements.\n\t\t\t *\n\t\t\t * @param {object} handle Handle returned by subscribe()\n\t\t\t */\n\t\t\tunsubscribe(handle) {\n\t\t\t\tif (!handle || !handle._livePlugin || handle._released) {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\thandle._released = true\n\n\t\t\t\t// Cancel any pending coalesced refetch before tearing down the\n\t\t\t\t// transport subscription — a late trailing dispatch after\n\t\t\t\t// unsubscribe would refetch for a scope nobody renders anymore.\n\t\t\t\tif (typeof handle._cancelPendingRefetch === 'function') {\n\t\t\t\t\thandle._cancelPendingRefetch()\n\t\t\t\t}\n\n\t\t\t\tconst liveUpdates = getLiveUpdates()\n\t\t\t\tliveUpdates.unsubscribe(handle.transportHandle)\n\n\t\t\t\tthis.liveSubscriptions = Math.max(0, this.liveSubscriptions - 1)\n\t\t\t},\n\t\t},\n\n\t\t/**\n\t\t * Plugin setup hook — called once per store instance.\n\t\t * Sets up:\n\t\t * 1. `$onAction` observer for fetchCollection → stash last params\n\t\t * 2. In-flight dedup Maps for fetchObject and fetchCollection\n\t\t *\n\t\t * @param {object} store The Pinia store instance\n\t\t */\n\t\tsetup(store) {\n\t\t\t// Dedup activation flag — flipped to true by the first subscribe()\n\t\t\t// call. Until then the fetch wrappers below pass straight through,\n\t\t\t// so a default-installed plugin causes zero behaviour change on\n\t\t\t// stores that never subscribe (live-updates-default-on).\n\t\t\tstore.__liveDedupActive = false\n\n\t\t\t// -- Last-params stash for collection re-fetch on events --\n\t\t\t// Plain (non-reactive) Map: type → last params object\n\t\t\tstore.__lastCollectionParams = new Map()\n\n\t\t\tstore.$onAction(({ name, args, after }) => {\n\t\t\t\tif (name === 'fetchCollection') {\n\t\t\t\t\tconst [type, params = {}] = args\n\t\t\t\t\tafter(() => {\n\t\t\t\t\t\tstore.__lastCollectionParams.set(type, params)\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t})\n\n\t\t\t// -- In-flight dedup for fetchObject --\n\t\t\t// Plain Map: dedupKey → Promise\n\t\t\tconst objectInFlight = new Map()\n\n\t\t\tconst originalFetchObject = store.fetchObject.bind(store)\n\t\t\tstore.fetchObject = async function dedupedFetchObject(type, id) {\n\t\t\t\tif (!store.__liveDedupActive) {\n\t\t\t\t\treturn originalFetchObject(type, id)\n\t\t\t\t}\n\t\t\t\tconst key = objectDedupKey(type, id)\n\t\t\t\tif (objectInFlight.has(key)) {\n\t\t\t\t\treturn objectInFlight.get(key)\n\t\t\t\t}\n\t\t\t\tconst promise = originalFetchObject(type, id).finally(() => {\n\t\t\t\t\tobjectInFlight.delete(key)\n\t\t\t\t})\n\t\t\t\tobjectInFlight.set(key, promise)\n\t\t\t\treturn promise\n\t\t\t}\n\n\t\t\t// -- In-flight dedup for fetchCollection --\n\t\t\tconst collectionInFlight = new Map()\n\n\t\t\tconst originalFetchCollection = store.fetchCollection.bind(store)\n\t\t\tstore.fetchCollection = async function dedupedFetchCollection(type, params = {}, options = {}) {\n\t\t\t\tif (!store.__liveDedupActive) {\n\t\t\t\t\treturn originalFetchCollection(type, params, options)\n\t\t\t\t}\n\t\t\t\tconst key = collectionDedupKey(type, params)\n\t\t\t\tlet entry = collectionInFlight.get(key)\n\t\t\t\tif (!entry) {\n\t\t\t\t\t// The shared request reports into its own sink, copied to\n\t\t\t\t\t// each caller's `options.outcome` below, so a caller that\n\t\t\t\t\t// joined it learns that request's result too.\n\t\t\t\t\tconst outcome = {}\n\t\t\t\t\tconst promise = originalFetchCollection(type, params, { ...options, outcome }).finally(() => {\n\t\t\t\t\t\tcollectionInFlight.delete(key)\n\t\t\t\t\t})\n\t\t\t\t\tentry = { promise, outcome }\n\t\t\t\t\tcollectionInFlight.set(key, entry)\n\t\t\t\t}\n\t\t\t\tconst results = await entry.promise\n\t\t\t\tconst sink = options && options.outcome\n\t\t\t\tif (sink && typeof sink === 'object' && 'error' in entry.outcome) {\n\t\t\t\t\tsink.error = entry.outcome.error\n\t\t\t\t}\n\t\t\t\treturn results\n\t\t\t}\n\t\t},\n\t}\n}\n"],"names":["_tryOnScopeDispose"],"mappings":";;;;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAcA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAAS,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE;AAC1C,CAAC,OAAO,CAAC,EAAE,IAAI,CAAC,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC;AAC3C,CAAC;AACD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAAS,cAAc,CAAC,IAAI,EAAE,EAAE,EAAE;AAClC,CAAC,OAAO,CAAC,EAAE,IAAI,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;AACvB,CAAC;AACD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAAS,mBAAmB,CAAC,EAAE,EAAE,MAAM,EAAE;AACzC,CAAC,IAAI,KAAK,GAAG,KAAI;AACjB,CAAC,IAAI,OAAO,GAAG,MAAK;AACpB;AACA,CAAC,SAAS,GAAG,GAAG;AAChB,EAAE,IAAI,EAAE,MAAM,GAAG,CAAC,CAAC,EAAE;AACrB,GAAG,EAAE,GAAE;AACP,GAAG,MAAM;AACT,GAAG;AACH,EAAE,IAAI,KAAK,EAAE;AACb,GAAG,OAAO,GAAG,KAAI;AACjB,GAAG,MAAM;AACT,GAAG;AACH,EAAE,EAAE,GAAE;AACN,EAAE,KAAK,GAAG,UAAU,CAAC,MAAM;AAC3B,GAAG,KAAK,GAAG,KAAI;AACf,GAAG,IAAI,OAAO,EAAE;AAChB,IAAI,OAAO,GAAG,MAAK;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"}
@@ -656,9 +656,12 @@ const baseActions = {
656
656
  *
657
657
  * @param {string} type The registered type slug
658
658
  * @param {object} [params] Query parameters (_limit, _page, _search, _order, filters)
659
+ * @param {object} [options] Call options
660
+ * @param {{error?: object|null}} [options.outcome] Opt-in sink for THIS call's own result: `error` is set to the error it records on `errors[type]`, or `null` on success. Unlike `errors[type]`, which every call for the type shares, a later call cannot overwrite it.
659
661
  * @return {Promise<Array>} The fetched collection (also stored in state)
660
662
  */
661
- async fetchCollection(type, params = {}) {
663
+ async fetchCollection(type, params = {}, options = {}) {
664
+ const outcome = (options && typeof options.outcome === 'object' && options.outcome) || null;
662
665
  this.loading = { ...this.loading, [type]: true };
663
666
  this.errors = { ...this.errors, [type]: null };
664
667
 
@@ -685,9 +688,13 @@ const baseActions = {
685
688
  );
686
689
 
687
690
  if (!response.ok) {
688
- this.errors = { ...this.errors, [type]: await parseResponseError(response, type) };
691
+ const failure = await parseResponseError(response, type);
692
+ this.errors = { ...this.errors, [type]: failure };
693
+ if (outcome) {
694
+ outcome.error = failure;
695
+ }
689
696
  // eslint-disable-next-line no-console -- diagnostic for a failure this code already degrades from
690
- console.error(`Error fetching ${type} collection:`, this.errors[type]);
697
+ console.error(`Error fetching ${type} collection:`, failure);
691
698
  return []
692
699
  }
693
700
 
@@ -711,14 +718,18 @@ const baseActions = {
711
718
  this.facets = { ...this.facets, [type]: transformed };
712
719
  }
713
720
 
721
+ if (outcome) {
722
+ outcome.error = null;
723
+ }
714
724
  return results
715
725
  } catch (error) {
716
- this.errors = {
717
- ...this.errors,
718
- [type]: error.name === 'TypeError'
719
- ? networkError(error)
720
- : genericError(error),
721
- };
726
+ const failure = error.name === 'TypeError'
727
+ ? networkError(error)
728
+ : genericError(error);
729
+ this.errors = { ...this.errors, [type]: failure };
730
+ if (outcome) {
731
+ outcome.error = failure;
732
+ }
722
733
  // eslint-disable-next-line no-console -- diagnostic for a failure this code already degrades from
723
734
  console.error(`Error fetching ${type} collection:`, error);
724
735
  return []