mnfst 0.5.247 → 0.5.248

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.
@@ -2,7 +2,7 @@
2
2
 
3
3
  (function () {
4
4
 
5
- const MANIFEST_BUILD_VERSION = '0.5.247';
5
+ const MANIFEST_BUILD_VERSION = '0.5.248';
6
6
 
7
7
  /* Manifest Data Sources - Configuration */
8
8
 
@@ -574,7 +574,8 @@ function writeSource(dataSourceName, data, options = {}) {
574
574
  error: options.error !== undefined ? options.error : currentState.error,
575
575
  ready: options.ready !== undefined ? options.ready : (data !== null && data !== undefined),
576
576
  errorTime: options.error !== undefined && options.error !== null ? Date.now() : (currentState.errorTime || null),
577
- stale: fresh ? false : currentState.stale !== false
577
+ stale: fresh ? false : currentState.stale !== false,
578
+ realtimeOverflow: currentState.realtimeOverflow || 0 // set via setSourceState (realtime caps)
578
579
  };
579
580
  store[`_${dataSourceName}_state`] = newState;
580
581
  if (fresh) markFresh(dataSourceName);
@@ -4923,8 +4924,8 @@ function createArrayProxyWithRoute(arrayTarget, dataSourceName = null, reloadDat
4923
4924
  return undefined;
4924
4925
  }
4925
4926
 
4926
- // Handle state properties ($loading, $error, $ready, $stale, $fresh)
4927
- if (key === '$stale' || key === '$fresh') {
4927
+ // Handle state properties ($loading, $error, $ready, $stale, $fresh, $realtimeOverflow)
4928
+ if (key === '$stale' || key === '$fresh' || key === '$realtimeOverflow') {
4928
4929
  return window.ManifestDataProxiesMagic?.getStateProperty?.(key, dataSourceName);
4929
4930
  }
4930
4931
  if (key === '$loading' || key === '$error' || key === '$ready') {
@@ -8816,7 +8817,7 @@ window.ManifestDataProxiesAppwrite.createAppwriteMethodsHandler = createAppwrite
8816
8817
 
8817
8818
 
8818
8819
  /* Manifest Data Sources - Magic Method State Properties */
8819
- // $loading, $error, $ready, $stale, $fresh state properties
8820
+ // $loading, $error, $ready, $stale, $fresh, $realtimeOverflow state properties
8820
8821
  //
8821
8822
  // $stale: true until the first network-fresh landing of this page-load is
8822
8823
  // applied (rows from a fetch, `$query`, or the memory cache of a fetch made
@@ -8824,12 +8825,15 @@ window.ManifestDataProxiesAppwrite.createAppwriteMethodsHandler = createAppwrite
8824
8825
  // reports through $loading. Single-reveal UIs gate on `!$stale`.
8825
8826
  // $fresh: promise resolving at that same first fresh landing; never rejects
8826
8827
  // (a failed reload keeps old rows and sets $error). Per page-load.
8828
+ // $realtimeOverflow: realtime events not applied since the last load (bounded
8829
+ // growth caps); reset to 0 on each load.
8827
8830
 
8828
- const STATE_PROPS = ['$loading', '$error', '$ready', '$stale', '$fresh'];
8831
+ const STATE_PROPS = ['$loading', '$error', '$ready', '$stale', '$fresh', '$realtimeOverflow'];
8829
8832
 
8830
8833
  function defaultStateValue(prop) {
8831
8834
  if (prop === '$error') return null;
8832
8835
  if (prop === '$stale') return true;
8836
+ if (prop === '$realtimeOverflow') return 0;
8833
8837
  return false;
8834
8838
  }
8835
8839
 
@@ -8858,6 +8862,8 @@ function getStateProperty(prop, dataSourceName) {
8858
8862
  return state.ready || false;
8859
8863
  } else if (prop === '$stale') {
8860
8864
  return state.stale !== false;
8865
+ } else if (prop === '$realtimeOverflow') {
8866
+ return state.realtimeOverflow || 0;
8861
8867
  }
8862
8868
 
8863
8869
  return undefined;
@@ -11705,10 +11711,10 @@ async function filterFilesByScope(files, scope) {
11705
11711
  }
11706
11712
 
11707
11713
  // Handle real-time storage events
11708
- async function handleStorageRealtimeEvent(dataSourceName, bucketId, scope, eventType, payload, generation) {
11714
+ async function handleStorageRealtimeEvent(dataSourceName, bucketId, scope, eventType, payload, generation, queriesConfig) {
11709
11715
 
11710
- // Deduplicate events
11711
- const eventKey = getEventKey(eventType, payload);
11716
+ // Deduplicate events (per source: shared-channel peers each process once)
11717
+ const eventKey = `${dataSourceName}|${getEventKey(eventType, payload)}`;
11712
11718
  if (isEventProcessed(eventKey)) {
11713
11719
  return;
11714
11720
  }
@@ -11731,8 +11737,12 @@ async function handleStorageRealtimeEvent(dataSourceName, bucketId, scope, event
11731
11737
  const file = payload?.$id ? payload : (payload?.file || payload);
11732
11738
  if (file && file.$id) {
11733
11739
  const fileMatchesScope = await checkFileMatchesScope(file, scope);
11734
- if (fileMatchesScope) {
11735
- landRows(dataSourceName, [file], { mode: 'append', generation });
11740
+ if (fileMatchesScope && eventMatchesQueries(file, queriesConfig)) {
11741
+ if (currentFiles.find(f => f && f.$id === file.$id)) {
11742
+ landRows(dataSourceName, [file], { mode: 'append', generation });
11743
+ } else {
11744
+ realtimeAppend(dataSourceName, file, currentFiles, generation);
11745
+ }
11736
11746
 
11737
11747
  // Emit custom event for new file creation so UI can refresh project files
11738
11748
  window.dispatchEvent(new CustomEvent('manifest:file-created', {
@@ -11746,10 +11756,14 @@ async function handleStorageRealtimeEvent(dataSourceName, bucketId, scope, event
11746
11756
  const file = payload?.$id ? payload : (payload?.file || payload);
11747
11757
  if (file && file.$id) {
11748
11758
  const fileMatchesScope = await checkFileMatchesScope(file, scope);
11749
- if (fileMatchesScope) {
11750
- landRows(dataSourceName, [file], { mode: 'append', generation });
11759
+ if (fileMatchesScope && eventMatchesQueries(file, queriesConfig)) {
11760
+ if (currentFiles.find(f => f && f.$id === file.$id)) {
11761
+ landRows(dataSourceName, [file], { mode: 'append', generation });
11762
+ } else {
11763
+ realtimeAppend(dataSourceName, file, currentFiles, generation);
11764
+ }
11751
11765
  } else {
11752
- // File no longer matches scope, remove it
11766
+ // File no longer matches scope/queries, remove it
11753
11767
  landRemove(dataSourceName, [file.$id], { generation });
11754
11768
  }
11755
11769
  }
@@ -11757,11 +11771,14 @@ async function handleStorageRealtimeEvent(dataSourceName, bucketId, scope, event
11757
11771
  const fileId = payload?.$id || payload?.file?.$id || payload?.fileId || payload;
11758
11772
  if (fileId) {
11759
11773
  landRemove(dataSourceName, [fileId.$id || fileId], { generation });
11774
+ forgetRealtimeRow(dataSourceName, fileId.$id || fileId);
11760
11775
  }
11761
11776
  }
11762
11777
  }
11763
11778
 
11764
- // Realtime event dedup: key by type+id+sequence(+timestamp)
11779
+ // Realtime event dedup: key by source+type+id+sequence(+timestamp).
11780
+ // Keyed PER SOURCE: on the shared realtime client two sources sharing a channel
11781
+ // each process an event once; the dedupe only absorbs replays within a source.
11765
11782
  const processedEvents = new Map(); // Map<eventKey, timestamp>
11766
11783
  const EVENT_DEDUP_WINDOW = 2000; // ms
11767
11784
 
@@ -11806,11 +11823,92 @@ function markEventProcessed(eventKey) {
11806
11823
  }
11807
11824
  }
11808
11825
 
11826
+ // Bounded realtime growth: per-source caps + overflow counter, reset each load
11827
+ const realtimeMeta = new Map(); // Map<name, { mode, maxRows, appended: [], pending: 0 }>
11828
+
11829
+ function configureRealtimeMeta(dataSourceName, config, initialCount, limitQuery) {
11830
+ const cfg = (config && typeof config === 'object') ? config : {};
11831
+ const mode = cfg.mode === 'window' ? 'window' : 'append';
11832
+ const fallback = Math.max(500, 2 * initialCount);
11833
+ let maxRows;
11834
+ if (typeof cfg.maxRows === 'number' && cfg.maxRows >= 0) {
11835
+ maxRows = cfg.maxRows; // explicit 0 = unbounded (legacy opt-in, append only)
11836
+ if (maxRows === 0 && mode === 'window') {
11837
+ console.warn(`[Manifest Data] realtime maxRows 0 contradicts window mode for "${dataSourceName}"; using the default cap`);
11838
+ maxRows = fallback;
11839
+ }
11840
+ } else if (mode === 'window') {
11841
+ // Window = the limit query, else the initial count; a computed 0 is no window, so default
11842
+ const windowSize = (typeof limitQuery === 'number' && limitQuery > 0) ? limitQuery : initialCount;
11843
+ maxRows = windowSize > 0 ? windowSize : fallback;
11844
+ } else {
11845
+ maxRows = fallback;
11846
+ }
11847
+ realtimeMeta.set(dataSourceName, { mode, maxRows, appended: [], pending: 0 });
11848
+ window.ManifestDataStore.setSourceState(dataSourceName, { realtimeOverflow: 0 });
11849
+ }
11850
+
11851
+ // Events not applied since the last load — $x.<source>.$realtimeOverflow
11852
+ function bumpRealtimeOverflow(dataSourceName) {
11853
+ const state = Alpine.store('data')?.[`_${dataSourceName}_state`];
11854
+ window.ManifestDataStore.setSourceState(dataSourceName, { realtimeOverflow: (state?.realtimeOverflow || 0) + 1 });
11855
+ }
11856
+
11857
+ // Append under the cap. 'window' evicts the oldest realtime-appended row first
11858
+ // (never an initially-loaded row); otherwise the event counts in $realtimeOverflow.
11859
+ function realtimeAppend(dataSourceName, row, currentRows, generation) {
11860
+ const { landRows, landRemove } = window.ManifestDataStore;
11861
+ const meta = realtimeMeta.get(dataSourceName);
11862
+ if (!meta || meta.maxRows === 0) {
11863
+ if (meta) meta.appended.push(row.$id);
11864
+ landRows(dataSourceName, [row], { mode: 'append', generation });
11865
+ return;
11866
+ }
11867
+ const len = currentRows.length + meta.pending;
11868
+ if (len >= meta.maxRows) {
11869
+ if (meta.mode !== 'window') {
11870
+ bumpRealtimeOverflow(dataSourceName);
11871
+ return;
11872
+ }
11873
+ let evicted = null;
11874
+ for (let i = 0; i < meta.appended.length; i++) {
11875
+ // Oldest appended row still landed; pending (unflushed) ids stay tracked
11876
+ if (currentRows.some(r => r && r.$id === meta.appended[i])) {
11877
+ evicted = meta.appended.splice(i, 1)[0];
11878
+ break;
11879
+ }
11880
+ }
11881
+ if (evicted === null) {
11882
+ bumpRealtimeOverflow(dataSourceName);
11883
+ return;
11884
+ }
11885
+ landRemove(dataSourceName, [evicted], { generation });
11886
+ }
11887
+ meta.pending++;
11888
+ meta.appended.push(row.$id);
11889
+ landRows(dataSourceName, [row], { mode: 'append', generation })
11890
+ .then(() => { meta.pending = Math.max(0, meta.pending - 1); });
11891
+ }
11892
+
11893
+ function forgetRealtimeRow(dataSourceName, id) {
11894
+ const meta = realtimeMeta.get(dataSourceName);
11895
+ if (!meta) return;
11896
+ const i = meta.appended.indexOf(id);
11897
+ if (i !== -1) meta.appended.splice(i, 1);
11898
+ }
11899
+
11900
+ // Query-aware event filter: the source's own queries, evaluated client-side
11901
+ // (unevaluable → match; see rowMatchesQueries)
11902
+ function eventMatchesQueries(row, queriesConfig) {
11903
+ const matcher = window.ManifestDataQueries?.rowMatchesQueries;
11904
+ return matcher ? matcher(row, queriesConfig) : true;
11905
+ }
11906
+
11809
11907
  // Handle real-time events for database tables
11810
- async function handleTableRealtimeEvent(dataSourceName, databaseId, tableId, scope, scopeColumns, eventType, payload, generation) {
11908
+ async function handleTableRealtimeEvent(dataSourceName, databaseId, tableId, scope, scopeColumns, eventType, payload, generation, queriesConfig) {
11811
11909
 
11812
- // Deduplicate events
11813
- const eventKey = getEventKey(eventType, payload);
11910
+ // Deduplicate events (per source: shared-channel peers each process once)
11911
+ const eventKey = `${dataSourceName}|${getEventKey(eventType, payload)}`;
11814
11912
  if (isEventProcessed(eventKey)) {
11815
11913
  return;
11816
11914
  }
@@ -11834,9 +11932,15 @@ async function handleTableRealtimeEvent(dataSourceName, databaseId, tableId, sco
11834
11932
  if (eventType === 'create') {
11835
11933
  const row = payload?.$id ? payload : (payload?.row || payload);
11836
11934
  if (row && row.$id) {
11935
+ // Scope AND queries must match before the row lands
11837
11936
  const rowMatchesScope = await checkRowMatchesScope(row, scope, scopeColumns);
11838
- if (rowMatchesScope) {
11839
- landRows(dataSourceName, [row], { mode: 'append', generation });
11937
+ if (rowMatchesScope && eventMatchesQueries(row, queriesConfig)) {
11938
+ if (currentRows.find(r => r.$id === row.$id)) {
11939
+ // Echo of a row already held (optimistic create): merge, never counts as growth
11940
+ landRows(dataSourceName, [row], { mode: 'append', generation });
11941
+ } else {
11942
+ realtimeAppend(dataSourceName, row, currentRows, generation);
11943
+ }
11840
11944
  }
11841
11945
  } else {
11842
11946
  console.warn('[Manifest Data] Invalid row payload in create event:', payload);
@@ -11846,14 +11950,17 @@ async function handleTableRealtimeEvent(dataSourceName, databaseId, tableId, sco
11846
11950
  if (row && row.$id) {
11847
11951
  const existingRow = currentRows.find(r => r.$id === row.$id);
11848
11952
  const rowMatchesScope = await checkRowMatchesScope(row, scope, scopeColumns);
11849
- if (!rowMatchesScope) {
11850
- // Row no longer matches scope, remove it
11851
- if (existingRow) landRemove(dataSourceName, [row.$id], { generation });
11953
+ if (!rowMatchesScope || !eventMatchesQueries(row, queriesConfig)) {
11954
+ // Row stopped matching scope or queries, remove it
11955
+ if (existingRow) {
11956
+ landRemove(dataSourceName, [row.$id], { generation });
11957
+ forgetRealtimeRow(dataSourceName, row.$id);
11958
+ }
11852
11959
  return;
11853
11960
  }
11854
11961
  if (!existingRow) {
11855
- // Row not in list, but matches scope now - add it
11856
- landRows(dataSourceName, [row], { mode: 'append', generation });
11962
+ // Row not in list, but matches now - add it (capped like a create)
11963
+ realtimeAppend(dataSourceName, row, currentRows, generation);
11857
11964
  return;
11858
11965
  }
11859
11966
 
@@ -11927,6 +12034,7 @@ async function handleTableRealtimeEvent(dataSourceName, databaseId, tableId, sco
11927
12034
  const rowId = payload?.$id || payload?.row?.$id || payload?.rowId || payload;
11928
12035
  if (rowId) {
11929
12036
  landRemove(dataSourceName, [rowId.$id || rowId], { generation });
12037
+ forgetRealtimeRow(dataSourceName, rowId.$id || rowId);
11930
12038
  }
11931
12039
  }
11932
12040
 
@@ -12226,15 +12334,19 @@ async function loadDataSource(dataSourceName, locale = 'en', options = {}) {
12226
12334
  );
12227
12335
  if (superseded()) { landed = true; return null; }
12228
12336
 
12229
- // Subscribe to real-time updates for this table (events from a superseded identity drop)
12230
- if (window.ManifestDataRealtime && window.ManifestDataRealtime.subscribeToTable) {
12337
+ // Subscribe to real-time updates for this table (events from a
12338
+ // superseded identity drop); realtime: false opts the source out
12339
+ if (dataSource.realtime !== false && window.ManifestDataRealtime && window.ManifestDataRealtime.subscribeToTable) {
12340
+ const rtQueries = Array.isArray(queriesConfig?.default || queriesConfig) ? (queriesConfig.default || queriesConfig) : [];
12341
+ const limitQ = rtQueries.find(q => Array.isArray(q) && q[0] === 'limit');
12342
+ configureRealtimeMeta(dataSourceName, dataSource.realtime, Array.isArray(data) ? data.length : 0, limitQ ? limitQ[1] : null);
12231
12343
  const unsubscribe = await window.ManifestDataRealtime.subscribeToTable(
12232
12344
  dataSourceName,
12233
12345
  appwriteConfig.databaseId,
12234
12346
  tableId,
12235
12347
  scope,
12236
12348
  async (eventType, payload) => {
12237
- await handleTableRealtimeEvent(dataSourceName, appwriteConfig.databaseId, tableId, scope, scopeColumns, eventType, payload, generation);
12349
+ await handleTableRealtimeEvent(dataSourceName, appwriteConfig.databaseId, tableId, scope, scopeColumns, eventType, payload, generation, rtQueries);
12238
12350
  },
12239
12351
  () => !superseded()
12240
12352
  );
@@ -12275,15 +12387,19 @@ async function loadDataSource(dataSourceName, locale = 'en', options = {}) {
12275
12387
 
12276
12388
  if (superseded()) { landed = true; return null; }
12277
12389
 
12278
- // Subscribe to real-time updates for this bucket (events from a superseded identity drop)
12279
- if (window.ManifestDataRealtime && window.ManifestDataRealtime.subscribeToStorageBucket) {
12390
+ // Subscribe to real-time updates for this bucket (events from a
12391
+ // superseded identity drop); realtime: false opts the source out
12392
+ if (dataSource.realtime !== false && window.ManifestDataRealtime && window.ManifestDataRealtime.subscribeToStorageBucket) {
12393
+ const rtQueries = Array.isArray(queriesConfig?.default || queriesConfig) ? (queriesConfig.default || queriesConfig) : [];
12394
+ const limitQ = rtQueries.find(q => Array.isArray(q) && q[0] === 'limit');
12395
+ configureRealtimeMeta(dataSourceName, dataSource.realtime, Array.isArray(data) ? data.length : 0, limitQ ? limitQ[1] : null);
12280
12396
  const unsubscribe = await window.ManifestDataRealtime.subscribeToStorageBucket(
12281
12397
  dataSourceName,
12282
12398
  bucketId,
12283
12399
  scope,
12284
12400
  async (eventType, payload) => {
12285
12401
  if (superseded()) return;
12286
- await handleStorageRealtimeEvent(dataSourceName, bucketId, scope, eventType, payload, generation);
12402
+ await handleStorageRealtimeEvent(dataSourceName, bucketId, scope, eventType, payload, generation, rtQueries);
12287
12403
  },
12288
12404
  () => !superseded()
12289
12405
  );
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "manifest.appwrite.auth.js": "sha384-dsIWnH0qCAGSzAXMGrd8fvOrVl4tOIE6j5tC35JPTrQXZ56qLD36ARyvhR7vi3Id",
3
- "manifest.appwrite.data.js": "sha384-sNtP9DeNzeKENXQRFUh1HC5t3SZNFJPbzc9bTuosCf181JbY4v8i3hjKenuP2ICr",
3
+ "manifest.appwrite.data.js": "sha384-cmzVdqqqKGSMnroBhHczc9m7W72E9O/fV2KEHRDgHkTiLTbZjJj2bAoaVbXtn3A8",
4
4
  "manifest.appwrite.presences.js": "sha384-JyVDWFT69b7Svq8J8p9tNWe2v9PZNQBY5UyhtOl1vnuHFAX0CRQ3MA9SiKq5wzNh",
5
5
  "manifest.bindings.js": "sha384-lPtMf8Ykaic9gNVZKq8Scia1881ZeMAyxer3WCIkUNwwrgzGBWto1OXADUwjvORF",
6
6
  "manifest.charts.js": "sha384-G+huD7UvjF9zDovdVVlh4I5X1Mq4oNnJfoFg4NnGdP3R/4Skq9hy5cGuHNYLxDYm",
@@ -11,7 +11,7 @@
11
11
  "manifest.combobox.js": "sha384-MILTkrgsTzsYUjGo4ZuADqV85Xs4GmfB4xexfe9vbJMIoQTZ42W4zs+DTaVSPEsx",
12
12
  "manifest.components.js": "sha384-BUFA8h+G5Plr82sV7bKzO42pPoiRZwRQcKj1lt2ROFiKjjlkayU/vmkAFqEsdBA+",
13
13
  "manifest.computed.js": "sha384-GYML/e81gQfN+iDbqhPUgny8bINCyL+SrbTofoGk4hlCnLLjv51mCv42JMqq+ku5",
14
- "manifest.data.js": "sha384-iaM1hIuvlArTLhJ2+79JpXpiRPusnKhldmHU00hFijo8kR8Ai1iKR3g5vuujCOBJ",
14
+ "manifest.data.js": "sha384-+1UOcfFJFXzjpwvvFiLB08B0++DrRbt/CssP8XuAmO+BdqqFfXafC1KmYLlzUEDC",
15
15
  "manifest.datepicker.js": "sha384-2XwFFnS4Bg5FDvG/v8x6KUvrjcBQEOYyTxynX2K/D/Qb3PI6+Y04lEwCnVDJLzwL",
16
16
  "manifest.defer.js": "sha384-pMRGqQKiEoJhpUcD40QeM4jEHau7sz9Xn/YCpiUamT1T4xJBZDSiYX+xnVrSWoq+",
17
17
  "manifest.device.js": "sha384-g+txmrEszYtuOvRelTp/c1wSNR2AtllVI9rx5NPPD7DzhVUnMNughonZykv2xlBW",
@@ -28,7 +28,7 @@
28
28
  "manifest.slides.js": "sha384-3uRTkyK9XPLmnxI2+igZlpi4EyPlU/7IHj5j3BZJJ2KN455vXyk99fiXV3feO/XY",
29
29
  "manifest.status.js": "sha384-MBO345ab7H6Uq1X2dZLt4Ge1Gcra+Z2qYApvlAeIV8CJhDDJrz2FPXpp+Kcp8zcM",
30
30
  "manifest.svg.js": "sha384-qr/8H9+2cIhSXNbRWRtIdrDlTOxKlNJqwEefHvCYoiBNW/kKhPTFa109Orv/EBnb",
31
- "manifest.sw.js": "sha384-61zm8U61fdgOmq50j9aAplOPmDAhK7OhIydJW4OdxEKjnSkMT5WNzWTqcrk3+UGc",
31
+ "manifest.sw.js": "sha384-sTcnAL2w7bznwSS8c7uFDuPd/ImDTZlO1FBddiOuR0iDaiPo0TGPGeYk0ZeWUbQ5",
32
32
  "manifest.tabs.js": "sha384-OIIcyaQnGS0eNYk4zgAFEDboNYect7THvUd9D0V6nqCeDOxQcoiqLrBEmmWVTTet",
33
33
  "manifest.tailwind.js": "sha384-r/Jvqb+Piz21jUts10BbVAJoKkShOdJRVFpywyi4HPIi/OfOJq5CtPyWAPbdkMgt",
34
34
  "manifest.toasts.js": "sha384-3zkWxzw693pxg3QDFsoYCA3CqYaLKmBaMArULXODD66Utolpewl0OAeTB92aWUVv",
@@ -36,5 +36,5 @@
36
36
  "manifest.url.parameters.js": "sha384-J2aSVBOPsoqsbafHndrWLHEDpKb4AKJAoQnVmKA4gZh5P/8HHjFWHO/lkusmG84l",
37
37
  "manifest.utilities.js": "sha384-L5uXMLFBIF6LUM1haRjd4eGjHSWuqsCYSpPZRPYosNfJsMC5U5TnmjBxIhrZRe2d",
38
38
  "manifest.virtual.js": "sha384-ZDNoF9QVhVcVpkjIK6NH15S6I5c6OBshiJpXSOYjNsIB+298I0a7g3aFO+jyq4Vb",
39
- "manifest.js": "sha384-AvD8QPahsOp5hmdBGJtfu2jp/dYrxLgapvAzx0oc2POlfb/Gj1tvFBHG+ct76p3d"
39
+ "manifest.js": "sha384-lLuq+77pwlGNK01XTOSimDh8boFBUmKQA8rR53QudXl/Igiv8MU4w+wRCSHdc/mg"
40
40
  }
package/lib/manifest.js CHANGED
@@ -456,9 +456,9 @@
456
456
  return getPluginUrlCandidates(pluginName, version)[0];
457
457
  }
458
458
 
459
- // Alpine URL candidates from a data-alpine value (version tag or full URL)
459
+ // Alpine URL candidates from a data-alpine value (version tag, full URL, or same-origin path)
460
460
  function alpineUrlCandidates(dataAlpine) {
461
- if (dataAlpine && dataAlpine.startsWith('http')) return [dataAlpine];
461
+ if (dataAlpine && (dataAlpine.startsWith('http') || dataAlpine.startsWith('/'))) return [dataAlpine];
462
462
  const v = dataAlpine || '3';
463
463
  return CDN_HOSTS.map(h => `${h}/alpinejs@${v}/dist/cdn.min.js`);
464
464
  }