mnfst 0.5.192 → 0.5.194

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.
@@ -364,10 +364,8 @@ function updateStore(dataSourceName, data, options = {}) {
364
364
  checkAndDispatchRenderReady();
365
365
  }
366
366
 
367
- // Attach methods to array if it's an array (for new architecture)
368
- // This ensures methods are available on the new array reference
367
+ // Re-attach methods on the new array reference
369
368
  if (Array.isArray(reactiveData) && window.ManifestDataProxies?.attachArrayMethods) {
370
- // Get the loadDataSource function from main module
371
369
  const loadDataSource = window.ManifestDataMain?.loadDataSource;
372
370
  if (loadDataSource) {
373
371
  window.ManifestDataProxies.attachArrayMethods(reactiveData, dataSourceName, loadDataSource);
@@ -397,8 +395,7 @@ function getRawData(dataSourceName) {
397
395
  return rawDataStore.get(dataSourceName);
398
396
  }
399
397
 
400
- // Create new object/array references for Alpine reactivity
401
- // This ensures nested arrays (like fileIds) get new references so Alpine can track changes
398
+ // New object/array references (incl. nested arrays like fileIds) so Alpine tracks changes
402
399
  function createReactiveReferences(data, dataSourceName = null) {
403
400
  if (data === null || data === undefined) {
404
401
  return data;
@@ -771,10 +768,7 @@ function setupTeamChangeListener() {
771
768
  }
772
769
  });
773
770
 
774
- // Actually reload the data sources (not just clear cache)
775
- // This ensures data is fresh when team changes
776
-
777
- // Reload each data source by calling loadDataSource directly
771
+ // Reload (not just cache-clear) so data is fresh for the new team
778
772
  const loadDataSource = window.ManifestDataMain?.loadDataSource;
779
773
  if (loadDataSource) {
780
774
  // Reload all team-scoped data sources with new team context
@@ -864,6 +858,16 @@ function checkAndDispatchRenderReady() {
864
858
  window.dispatchEvent(new CustomEvent('manifest:render-ready', {
865
859
  detail: { locale, sources }
866
860
  }));
861
+ // Alpine 3.x can strand effects re-queued mid-flush (scheduler
862
+ // swallow) — an x-if that read this store before data arrived may
863
+ // never re-run. One post-settle bump on a fresh task re-runs
864
+ // anything dropped.
865
+ setTimeout(() => {
866
+ try {
867
+ const s = Alpine.store('data');
868
+ if (s) Alpine.store('data', { ...s, _dataVersion: (s._dataVersion || 0) + 1 });
869
+ } catch (_) { /* no-op */ }
870
+ }, 50);
867
871
  } catch {
868
872
  // Silently fail — the render script has its own timeout fallback
869
873
  }
@@ -964,8 +968,7 @@ function setupLocaleChangeListener() {
964
968
  }
965
969
  promisesToDelete.forEach(key => loadingPromises.delete(key));
966
970
 
967
- // Clear nested proxy cache for this data source
968
- // This ensures fresh proxies are created with new locale data
971
+ // Clear nested proxy cache so fresh proxies use the new locale data
969
972
  if (window.ManifestDataProxies?.clearNestedProxyCacheForDataSource) {
970
973
  window.ManifestDataProxies.clearNestedProxyCacheForDataSource(dataSourceName);
971
974
  }
@@ -1795,9 +1798,7 @@ window.ManifestDataLoaders = {
1795
1798
  };
1796
1799
 
1797
1800
  /* Manifest Data Sources - Cloud API Loader */
1798
- // NOTE: This is basic read-only API support included in core for localization compatibility.
1799
- // Full CRUD operations will be available via manifest.api.data.js plugin (planned).
1800
- // When the API plugin is available, it will extend this functionality.
1801
+ // Basic read-only API support, included in core for localization compatibility
1801
1802
 
1802
1803
  // Load from API endpoint (read-only)
1803
1804
  async function loadFromAPI(dataSource) {
@@ -4258,21 +4259,17 @@ if (typeof window !== 'undefined') {
4258
4259
 
4259
4260
 
4260
4261
  /* Manifest Data Sources - Object Proxy Creation */
4261
- // Create proxies for nested objects that properly handles arrays and further nesting
4262
- // Simplified: Only proxy arrays, return objects directly (like backup)
4262
+ // Proxies nested objects/arrays for $x access. path = keys from the source root,
4263
+ // used to resolve values from the raw store without triggering Alpine reactivity.
4264
+ // Note (applies throughout): Alpine may wrap our proxies in its own, so always read
4265
+ // via rawTarget/the raw store — never `target` — and cache one proxy per raw object,
4266
+ // or Alpine sees a "new" object each access and re-evaluates forever.
4263
4267
  function createNestedObjectProxy(objTarget, dataSourceName = null, reloadDataSource = null, path = []) {
4264
- // path: array of keys representing the path to this object (e.g., ['specialHeader'] for $x.example.specialHeader)
4265
- // This allows us to directly access nested values in the raw store without triggering Alpine reactivity
4266
-
4267
- // CRITICAL: Check if objTarget is already a proxy we created to prevent infinite recursion
4268
- // Alpine may wrap our proxy, but we should never proxy a proxy we already created
4269
4268
  if (window.ManifestDataProxiesCore?.nestedObjectProxyCache?.has(objTarget)) {
4270
- // This object is already proxied, return the cached proxy
4271
4269
  return window.ManifestDataProxiesCore.nestedObjectProxyCache.get(objTarget);
4272
4270
  }
4273
4271
 
4274
- // Get the raw object from the store using the path to ensure we cache the correct object
4275
- // This is critical because objTarget might be Alpine-wrapped (e.g. when raw wasn't ready at first access)
4272
+ // Resolve the raw object from the store by path (objTarget may be Alpine-wrapped)
4276
4273
  let rawObjectForCache = objTarget;
4277
4274
  if (path.length >= 0 && window.ManifestDataStore?.getRawData && dataSourceName) {
4278
4275
  const rawDataSource = window.ManifestDataStore.getRawData(dataSourceName);
@@ -4294,19 +4291,14 @@ function createNestedObjectProxy(objTarget, dataSourceName = null, reloadDataSou
4294
4291
  }
4295
4292
  }
4296
4293
 
4297
- // Check cache first to prevent infinite recursion and ensure same proxy instance is returned
4298
- // This is critical for Alpine reactivity - if we create a new proxy each time,
4299
- // Alpine sees it as a "new" object and triggers re-evaluation, causing infinite loops
4300
- // Use the raw object from store as cache key (WeakMap requires object keys)
4294
+ // Cache check keyed by raw object
4301
4295
  if (window.ManifestDataProxiesCore?.nestedObjectProxyCache?.has(rawObjectForCache)) {
4302
4296
  const cached = window.ManifestDataProxiesCore.nestedObjectProxyCache.get(rawObjectForCache);
4303
4297
 
4304
4298
  return cached;
4305
4299
  }
4306
4300
 
4307
- // Track active property accesses to prevent circular references
4308
- // Use a WeakMap to track active accesses per target object (works even with Alpine proxies)
4309
- // CRITICAL: Use rawObjectForCache as the key, not objTarget, because objTarget might be Alpine-wrapped
4301
+ // Active property accesses per raw target (circular-reference guard)
4310
4302
  if (!window.ManifestDataProxiesCore) {
4311
4303
  window.ManifestDataProxiesCore = {};
4312
4304
  }
@@ -4315,22 +4307,13 @@ function createNestedObjectProxy(objTarget, dataSourceName = null, reloadDataSou
4315
4307
  }
4316
4308
  const activePropsMap = window.ManifestDataProxiesCore.nestedProxyActiveProps;
4317
4309
 
4318
- // Initialize active props set for this target if not already present
4319
- // Use rawObjectForCache as key to ensure consistency even when Alpine wraps the proxy
4320
4310
  if (!activePropsMap.has(rawObjectForCache)) {
4321
4311
  activePropsMap.set(rawObjectForCache, new Set());
4322
4312
  }
4323
4313
 
4324
- // Store reference to raw target to avoid Alpine proxy wrapping issues
4325
- // CRITICAL: Alpine may wrap our proxy in its own proxy, making 'target' in the get trap
4326
- // actually be Alpine's wrapped version. By storing the raw object reference separately,
4327
- // we can always access the true raw data without triggering Alpine's reactivity
4328
- // CRITICAL: Always use rawObjectForCache for tracking, not objTarget which might be Alpine-wrapped
4314
+ // rawTarget: the true raw data (see header note); also used as the Proxy target
4329
4315
  const rawTarget = rawObjectForCache;
4330
4316
 
4331
- // CRITICAL: Use rawTarget as the Proxy target, not objTarget
4332
- // This ensures Alpine wraps a proxy around raw data, not Alpine-wrapped data
4333
- // When Alpine wraps our proxy and accesses properties, it won't trigger reactivity loops
4334
4317
  const proxyTarget = rawTarget;
4335
4318
 
4336
4319
  // Track call depth for debugging recursion
@@ -4377,7 +4360,6 @@ function createNestedObjectProxy(objTarget, dataSourceName = null, reloadDataSou
4377
4360
  } catch (e) { /* ignore */ }
4378
4361
  }
4379
4362
 
4380
- // CRITICAL: If rawTarget is already a proxy we created, get value directly from store
4381
4363
  const isRawTargetProxied = window.ManifestDataProxiesCore?.nestedObjectProxyCache?.has(rawTarget);
4382
4364
 
4383
4365
  // Required by handleCircularReference (no debug stack capture)
@@ -4391,13 +4373,12 @@ function createNestedObjectProxy(objTarget, dataSourceName = null, reloadDataSou
4391
4373
  // Handle toPrimitive for text content
4392
4374
  if (key === Symbol.toPrimitive) {
4393
4375
  return function () {
4394
- // Use rawTarget (rawObjectForCache) to avoid Alpine reactivity issues
4395
4376
  try {
4396
4377
  const getRawData = window.ManifestDataStore?.getRawData;
4397
4378
  if (getRawData && dataSourceName && path.length >= 0) {
4398
4379
  const rawDataSource = getRawData(dataSourceName);
4399
4380
  if (rawDataSource && typeof rawDataSource === 'object') {
4400
- // Use a helper function to safely access properties without triggering proxies
4381
+ // Safe property access without triggering proxies
4401
4382
  const safeGet = (obj, prop) => {
4402
4383
  if (obj && typeof obj === 'object' && prop in obj) {
4403
4384
  try {
@@ -4414,7 +4395,6 @@ function createNestedObjectProxy(objTarget, dataSourceName = null, reloadDataSou
4414
4395
 
4415
4396
  let current = rawDataSource;
4416
4397
  let pathValid = true;
4417
- // Traverse the full path including the current key to get the final value
4418
4398
  const fullPath = [...path, key];
4419
4399
  for (let i = 0; i < fullPath.length; i++) {
4420
4400
  const pathKey = fullPath[i];
@@ -4438,13 +4418,10 @@ function createNestedObjectProxy(objTarget, dataSourceName = null, reloadDataSou
4438
4418
  };
4439
4419
  }
4440
4420
 
4441
- // Check for circular reference using WeakMap
4442
- // Use rawTarget (rawObjectForCache) instead of target to ensure we're tracking the correct object
4443
- // This is critical because target might be Alpine's wrapped version
4421
+ // Circular reference check
4444
4422
  const activeProps = activePropsMap.get(rawTarget);
4445
4423
  const propKey = String(key);
4446
4424
 
4447
- // Use extracted circular reference handler
4448
4425
  const handleCircularReference = window.ManifestDataProxiesHandlers?.handleCircularReference;
4449
4426
  if (handleCircularReference) {
4450
4427
  const circularResult = handleCircularReference({
@@ -4458,13 +4435,12 @@ function createNestedObjectProxy(objTarget, dataSourceName = null, reloadDataSou
4458
4435
  triggeredBy,
4459
4436
  shouldLog: false,
4460
4437
  });
4461
- // If handler returned a value (including undefined), use it
4462
- // null means not a circular reference, continue normal flow
4438
+ // null means not circular; any other value (incl. undefined) is the result
4463
4439
  if (circularResult !== null) {
4464
4440
  return circularResult;
4465
4441
  }
4466
4442
  } else {
4467
- // Fallback to inline handling if handler not available (shouldn't happen in production)
4443
+ // Inline fallback if handler unavailable
4468
4444
  if (activeProps && activeProps.has(propKey)) {
4469
4445
  if (activeProps) {
4470
4446
  activeProps.delete(propKey);
@@ -4473,34 +4449,23 @@ function createNestedObjectProxy(objTarget, dataSourceName = null, reloadDataSou
4473
4449
  }
4474
4450
  }
4475
4451
 
4476
- // Mark this property as being accessed (temporarily, will remove after getting value)
4477
- // CRITICAL: Do this BEFORE any async operations or proxy creation to prevent re-entry
4452
+ // Mark property as in-flight before any proxy creation (re-entry guard)
4478
4453
  if (activeProps) {
4479
4454
  activeProps.add(propKey);
4480
4455
  }
4481
4456
 
4482
- // CRITICAL: Always use rawTarget directly, never target
4483
- // Even though we set proxyTarget to rawTarget, Alpine may wrap our proxy
4484
- // and replace 'target' with an Alpine-wrapped version
4485
- // By always using rawTarget (captured in closure), we ensure we're accessing raw data
4486
4457
  let value;
4487
4458
 
4488
4459
  try {
4489
- // Use a safe property accessor that bypasses proxies
4490
4460
  const safeGet = (obj, prop) => {
4491
4461
  if (!obj || typeof obj !== 'object') return undefined;
4492
- // Use Object.prototype.hasOwnProperty to check existence without triggering getters
4493
4462
  if (Object.prototype.hasOwnProperty.call(obj, prop)) {
4494
- // Use direct property access - this bypasses proxy getters
4495
4463
  return obj[prop];
4496
4464
  }
4497
4465
  return undefined;
4498
4466
  };
4499
4467
 
4500
- // ALWAYS use rawTarget, never target (which might be Alpine-wrapped)
4501
- // For nested paths, traverse step by step using safe property access
4502
- // rawTarget is always the object at this path (path from root to this proxy).
4503
- // So we only need to read the requested key from rawTarget, never re-traverse path.
4468
+ // rawTarget is already the object at `path`; just read the requested key
4504
4469
  value = safeGet(rawTarget, key);
4505
4470
  } catch (e) {
4506
4471
  // Silently handle errors
@@ -4528,23 +4493,19 @@ function createNestedObjectProxy(objTarget, dataSourceName = null, reloadDataSou
4528
4493
  return fallback !== undefined ? fallback : '';
4529
4494
  }
4530
4495
 
4531
- // CRITICAL: Return primitives immediately to prevent Alpine wrapping issues
4532
- // This must come BEFORE array/object checks to handle primitive values correctly
4496
+ // Primitives return immediately (must precede array/object checks)
4533
4497
  if (value === null ||
4534
4498
  typeof value === 'string' || typeof value === 'number' ||
4535
4499
  typeof value === 'boolean' || typeof value === 'symbol') {
4536
- // Remove from activeProps before returning
4537
4500
  if (activeProps) {
4538
4501
  activeProps.delete(propKey);
4539
4502
  }
4540
- // Reset depth after returning primitive
4541
4503
  callDepthMap.delete(rawTarget);
4542
4504
  return value;
4543
4505
  }
4544
4506
 
4545
- // If the property is an array, create a proxy that handles array methods and $route at the top level
4507
+ // Arrays: proxy with array methods and $route handled at the top level
4546
4508
  if (Array.isArray(value)) {
4547
- // First attach methods directly to the array (for compatibility)
4548
4509
  let arrayWithMethods = value;
4549
4510
  try {
4550
4511
  const attachArrayMethods = window.ManifestDataProxies?.attachArrayMethods;
@@ -4555,19 +4516,17 @@ function createNestedObjectProxy(objTarget, dataSourceName = null, reloadDataSou
4555
4516
  // Silently handle error attaching methods
4556
4517
  }
4557
4518
 
4558
- // CRITICAL: Store reference to key and dataSourceName for toJSON access
4559
4519
  const arrayKey = key;
4560
4520
  const arrayDataSourceName = dataSourceName;
4561
4521
 
4562
- // CRITICAL: Define toJSON on the array before proxying
4563
- // JSON.stringify checks for toJSON before accessing properties
4522
+ // toJSON must exist before proxying — JSON.stringify checks it first
4564
4523
  if (typeof arrayWithMethods.toJSON !== 'function') {
4565
4524
  Object.defineProperty(arrayWithMethods, 'toJSON', {
4566
4525
  enumerable: false,
4567
4526
  configurable: true,
4568
4527
  writable: false,
4569
4528
  value: function () {
4570
- // Get raw array from store for serialization
4529
+ // Serialize from the raw store array
4571
4530
  try {
4572
4531
  const getRawData = window.ManifestDataStore?.getRawData;
4573
4532
  if (getRawData && arrayDataSourceName) {
@@ -4575,35 +4534,28 @@ function createNestedObjectProxy(objTarget, dataSourceName = null, reloadDataSou
4575
4534
  if (rawDataSource && typeof rawDataSource === 'object') {
4576
4535
  const rawArray = rawDataSource[arrayKey];
4577
4536
  if (Array.isArray(rawArray)) {
4578
- return rawArray; // Return raw array directly
4537
+ return rawArray;
4579
4538
  }
4580
4539
  }
4581
4540
  }
4582
4541
  } catch (e) {
4583
4542
  // Fallback
4584
4543
  }
4585
- // Fallback to arrayWithMethods (should be a plain array)
4586
4544
  return Array.isArray(arrayWithMethods) ? arrayWithMethods : arrayWithMethods;
4587
4545
  }
4588
4546
  });
4589
4547
  }
4590
4548
 
4591
- // Create a proxy for the array that handles methods at the top level
4592
- // This is similar to how Appwrite methods work - handled in proxy's get trap
4593
4549
  const arrayProxy = new Proxy(arrayWithMethods, {
4594
4550
  get(proxyTarget, prop) {
4595
- // CRITICAL: Handle toJSON for JSON.stringify compatibility
4596
- // JSON.stringify calls toJSON if it exists, otherwise it accesses properties
4597
4551
  if (prop === 'toJSON') {
4598
- // Return the toJSON method directly from the target
4599
4552
  return proxyTarget.toJSON;
4600
4553
  }
4601
4554
 
4602
- // CRITICAL: Handle Symbol.toPrimitive for string conversion
4555
+ // String conversion
4603
4556
  if (prop === Symbol.toPrimitive) {
4604
4557
  return function (hint) {
4605
4558
  if (hint === 'string' || hint === 'default') {
4606
- // Use toJSON if available, otherwise stringify directly
4607
4559
  if (typeof proxyTarget.toJSON === 'function') {
4608
4560
  return JSON.stringify(proxyTarget.toJSON());
4609
4561
  }
@@ -4613,20 +4565,16 @@ function createNestedObjectProxy(objTarget, dataSourceName = null, reloadDataSou
4613
4565
  };
4614
4566
  }
4615
4567
 
4616
- // Handle $search and $query at proxy level with safe fallbacks
4568
+ // $search/$query with empty-array fallback while loading
4617
4569
  if (prop === '$search' || prop === '$query') {
4618
- // Check if method exists on target
4619
4570
  if (proxyTarget && typeof proxyTarget === 'object' && prop in proxyTarget && typeof proxyTarget[prop] === 'function') {
4620
4571
  return proxyTarget[prop].bind(proxyTarget);
4621
4572
  }
4622
- // Fallback: return safe function that returns empty array
4623
- // This prevents Alpine errors when method doesn't exist yet (during loading)
4624
4573
  return function () {
4625
4574
  return [];
4626
4575
  };
4627
4576
  }
4628
4577
 
4629
- // Handle $route at proxy level (like Appwrite methods)
4630
4578
  if (prop === '$route') {
4631
4579
  const createRouteProxy = window.ManifestDataProxies?.createRouteProxy;
4632
4580
  if (!createRouteProxy) {
@@ -4636,11 +4584,9 @@ function createNestedObjectProxy(objTarget, dataSourceName = null, reloadDataSou
4636
4584
  if (proxyTarget && Array.isArray(proxyTarget)) {
4637
4585
  const getRawData = window.ManifestDataStore?.getRawData;
4638
4586
  let dataToUse = proxyTarget;
4639
- // Try to get the nested array from raw data if needed
4640
4587
  if (dataSourceName && getRawData) {
4641
4588
  const rawData = getRawData(dataSourceName);
4642
4589
  if (rawData && typeof rawData === 'object' && !Array.isArray(rawData)) {
4643
- // If raw data is an object, try to find the nested array
4644
4590
  if (rawData[key] && Array.isArray(rawData[key])) {
4645
4591
  dataToUse = rawData[key];
4646
4592
  }
@@ -4659,16 +4605,15 @@ function createNestedObjectProxy(objTarget, dataSourceName = null, reloadDataSou
4659
4605
  }
4660
4606
  return new Proxy({}, { get: () => undefined });
4661
4607
  };
4662
- // Ensure function has proper prototype for Alpine's instanceof checks
4608
+ // Proper Function prototype for Alpine's instanceof checks
4663
4609
  Object.setPrototypeOf(routeFunction, Function.prototype);
4664
- // Mark as callable
4665
4610
  routeFunction.call = Function.prototype.call;
4666
4611
  routeFunction.apply = Function.prototype.apply;
4667
4612
  routeFunction.bind = Function.prototype.bind;
4668
4613
  return routeFunction;
4669
4614
  }
4670
4615
 
4671
- // Handle ALL array methods at proxy level (like Appwrite methods)
4616
+ // Array methods bound to the target
4672
4617
  if (typeof prop === 'string' && typeof Array.prototype[prop] === 'function') {
4673
4618
  if (typeof proxyTarget[prop] === 'function') {
4674
4619
  const bound = proxyTarget[prop].bind(proxyTarget);
@@ -4680,12 +4625,11 @@ function createNestedObjectProxy(objTarget, dataSourceName = null, reloadDataSou
4680
4625
  return bound;
4681
4626
  }
4682
4627
 
4683
- // Fall through to target for other properties (including numeric indices)
4628
+ // Fall through (including numeric indices)
4684
4629
  return proxyTarget[prop];
4685
4630
  },
4686
- // CRITICAL: Alpine uses has() to check if properties exist before accessing them
4631
+ // Alpine checks has() before property access
4687
4632
  has(target, prop) {
4688
- // Always report that base plugin methods exist (we provide fallbacks)
4689
4633
  if (prop === '$route' || prop === '$search' || prop === '$query') {
4690
4634
  return true;
4691
4635
  }
@@ -4694,10 +4638,9 @@ function createNestedObjectProxy(objTarget, dataSourceName = null, reloadDataSou
4694
4638
  }
4695
4639
  return prop in target;
4696
4640
  },
4697
- // CRITICAL: Alpine uses getOwnPropertyDescriptor to introspect properties
4641
+ // Alpine introspects via getOwnPropertyDescriptor; mirror get()
4698
4642
  getOwnPropertyDescriptor(target, prop) {
4699
4643
  if (prop === '$route') {
4700
- // Create the same function as in get() to ensure consistency
4701
4644
  const createRouteProxy = window.ManifestDataProxies?.createRouteProxy;
4702
4645
  if (!createRouteProxy) {
4703
4646
  return {
@@ -4759,14 +4702,13 @@ function createNestedObjectProxy(objTarget, dataSourceName = null, reloadDataSou
4759
4702
  }
4760
4703
  return Reflect.getOwnPropertyDescriptor(target, prop);
4761
4704
  },
4762
- // CRITICAL: Include $route in ownKeys so Alpine sees it as an own property
4705
+ // $route/toJSON must appear as own keys for Alpine and JSON.stringify
4763
4706
  ownKeys(target) {
4764
4707
  const keys = Reflect.ownKeys(target);
4765
4708
  const result = [...keys];
4766
4709
  if (!result.includes('$route')) {
4767
4710
  result.push('$route');
4768
4711
  }
4769
- // CRITICAL: Include toJSON for JSON.stringify compatibility
4770
4712
  if (!result.includes('toJSON')) {
4771
4713
  result.push('toJSON');
4772
4714
  }
@@ -4774,7 +4716,6 @@ function createNestedObjectProxy(objTarget, dataSourceName = null, reloadDataSou
4774
4716
  }
4775
4717
  });
4776
4718
 
4777
- // Remove from activeProps after creating array proxy
4778
4719
  if (activeProps) {
4779
4720
  activeProps.delete(propKey);
4780
4721
  }
@@ -4782,22 +4723,11 @@ function createNestedObjectProxy(objTarget, dataSourceName = null, reloadDataSou
4782
4723
  return arrayProxy;
4783
4724
  }
4784
4725
 
4785
- // If the property is an object, wrap it recursively for further nesting
4786
- // (activeProps is already declared above)
4787
- // Pass along dataSourceName and reloadDataSource to maintain context
4726
+ // Objects: wrap recursively for further nesting
4788
4727
  if (typeof value === 'object' && value !== null) {
4789
- // NOTE: activeProps was already removed above after getting the value
4790
- // This prevents false circular reference detection when Alpine wraps our proxy
4791
-
4792
- // CRITICAL: Always use the raw value we got from the store using path-based access
4793
- // The 'value' variable already contains the raw data from the store (via path traversal)
4794
- // This ensures we never use Alpine-wrapped objects
4795
4728
  let objectToProxy = value;
4796
4729
 
4797
- // CRITICAL FIX: For simple objects accessed through nested proxies,
4798
- // return a plain object copy instead of creating another proxy.
4799
- // This prevents infinite recursion when Alpine wraps our proxy and accesses properties.
4800
- // Use extracted simple object handler
4730
+ // Simple objects return as plain copies instead of proxies (recursion guard)
4801
4731
  const handleSimpleObject = window.ManifestDataProxiesSimple?.handleSimpleObject;
4802
4732
  if (handleSimpleObject && !Array.isArray(value)) {
4803
4733
  const plainCopy = handleSimpleObject(value, {
@@ -4807,17 +4737,14 @@ function createNestedObjectProxy(objTarget, dataSourceName = null, reloadDataSou
4807
4737
  fullPath,
4808
4738
  callDepthMap
4809
4739
  });
4810
- // If handler returned a plain copy, use it (null means not a simple object or failed)
4740
+ // null means not a simple object; fall through to proxy creation
4811
4741
  if (plainCopy !== null) {
4812
4742
  return plainCopy;
4813
4743
  }
4814
- // Fall through to proxy creation if not a simple object or copy failed
4815
4744
  }
4816
4745
 
4817
- // CRITICAL: If rawTarget is already proxied, check if the value itself is proxied before creating new proxy
4818
- // This prevents infinite recursion when Alpine wraps our proxy and accesses nested properties
4746
+ // If rawTarget is already proxied, resolve the nested raw object and reuse its cached proxy
4819
4747
  if (isRawTargetProxied) {
4820
- // Get the raw nested object from store to check cache
4821
4748
  const newPath = [...path, key];
4822
4749
  let rawNestedObject = objectToProxy;
4823
4750
 
@@ -4846,7 +4773,6 @@ function createNestedObjectProxy(objTarget, dataSourceName = null, reloadDataSou
4846
4773
  // Silently handle errors
4847
4774
  }
4848
4775
 
4849
- // If the nested object is already proxied, return the cached proxy
4850
4776
  if (window.ManifestDataProxiesCore?.nestedObjectProxyCache?.has(rawNestedObject)) {
4851
4777
  const cachedProxy = window.ManifestDataProxiesCore.nestedObjectProxyCache.get(rawNestedObject);
4852
4778
  if (cachedProxy) {
@@ -4857,13 +4783,9 @@ function createNestedObjectProxy(objTarget, dataSourceName = null, reloadDataSou
4857
4783
  }
4858
4784
  }
4859
4785
 
4860
- // If rawTarget is proxied but nested object isn't, we still need to create a proxy
4861
- // But use the raw nested object from store as the target
4862
4786
  objectToProxy = rawNestedObject;
4863
4787
  }
4864
4788
 
4865
- // CRITICAL: Check if this object is already a proxy we created to prevent infinite recursion
4866
- // This can happen when Alpine wraps our proxy and accesses properties on the wrapped proxy
4867
4789
  if (window.ManifestDataProxiesCore?.nestedObjectProxyCache?.has(objectToProxy)) {
4868
4790
  const cachedProxy = window.ManifestDataProxiesCore.nestedObjectProxyCache.get(objectToProxy);
4869
4791
  if (activeProps) {
@@ -4872,17 +4794,14 @@ function createNestedObjectProxy(objTarget, dataSourceName = null, reloadDataSou
4872
4794
  return cachedProxy;
4873
4795
  }
4874
4796
 
4875
- // Final check: if still undefined or null, return undefined
4876
4797
  if (objectToProxy === undefined || objectToProxy === null) {
4877
4798
  return undefined;
4878
4799
  }
4879
4800
 
4880
- // CRITICAL FIX: Check cache BEFORE calling createNestedObjectProxy to avoid function call overhead
4881
- // Get the raw nested object from store to use as cache key
4801
+ // Cache check before createNestedObjectProxy (avoids call overhead)
4882
4802
  const newPath = [...path, key];
4883
4803
  let rawNestedObject = objectToProxy;
4884
4804
 
4885
- // Try to get the raw object from store using the new path
4886
4805
  const getRawData = window.ManifestDataStore?.getRawData;
4887
4806
  if (getRawData && dataSourceName) {
4888
4807
  const rawDataSource = getRawData(dataSourceName);
@@ -4907,7 +4826,6 @@ function createNestedObjectProxy(objTarget, dataSourceName = null, reloadDataSou
4907
4826
  if (window.ManifestDataProxiesCore?.nestedObjectProxyCache?.has(rawNestedObject)) {
4908
4827
  const cachedProxy = window.ManifestDataProxiesCore.nestedObjectProxyCache.get(rawNestedObject);
4909
4828
  if (cachedProxy) {
4910
- // Remove from activeProps before returning cached proxy
4911
4829
  if (activeProps) {
4912
4830
  activeProps.delete(propKey);
4913
4831
  }
@@ -4917,33 +4835,27 @@ function createNestedObjectProxy(objTarget, dataSourceName = null, reloadDataSou
4917
4835
 
4918
4836
  const nestedProxy = createNestedObjectProxy(objectToProxy, dataSourceName, reloadDataSource, newPath);
4919
4837
 
4920
- // Remove from activeProps after creating nested proxy
4921
- // This allows Alpine to access nested properties without false circular detection
4922
4838
  if (activeProps) {
4923
4839
  activeProps.delete(propKey);
4924
4840
  }
4925
4841
 
4926
- // Don't reset depth here - nested proxy will handle it
4842
+ // Depth reset handled by the nested proxy
4927
4843
  return nestedProxy;
4928
4844
  }
4929
4845
 
4930
- // If value is undefined, return a loading proxy to maintain chain and prevent errors
4846
+ // Undefined: loading proxy keeps the chain alive
4931
4847
  if (value === undefined) {
4932
- // Remove from activeProps before returning
4933
4848
  if (activeProps) {
4934
4849
  activeProps.delete(propKey);
4935
4850
  }
4936
- // Reset depth
4937
4851
  callDepthMap.delete(rawTarget);
4938
4852
  return window.ManifestDataProxiesCore.createLoadingProxy(dataSourceName);
4939
4853
  }
4940
4854
 
4941
- // Remove from activeProps before returning
4942
4855
  if (activeProps) {
4943
4856
  activeProps.delete(propKey);
4944
4857
  }
4945
4858
 
4946
- // Reset depth
4947
4859
  callDepthMap.delete(rawTarget);
4948
4860
  return value;
4949
4861
  } finally {
@@ -4952,9 +4864,7 @@ function createNestedObjectProxy(objTarget, dataSourceName = null, reloadDataSou
4952
4864
  }
4953
4865
  });
4954
4866
 
4955
- // Cache the proxy before returning to prevent re-proxying the same object
4956
- // Use the raw object from store as cache key (WeakMap requires object keys)
4957
- // Critical for Alpine reactivity - prevents infinite re-evaluation loops
4867
+ // Cache keyed by raw object (see header note)
4958
4868
  if (window.ManifestDataProxiesCore?.nestedObjectProxyCache) {
4959
4869
  window.ManifestDataProxiesCore.nestedObjectProxyCache.set(rawObjectForCache, proxy);
4960
4870
 
@@ -5700,7 +5610,6 @@ function createReactiveFileManager(tableName, entryId, bucketName, columnName) {
5700
5610
  return existing;
5701
5611
  }
5702
5612
 
5703
- // CRITICAL: Make files array reactive so Alpine can track changes
5704
5613
  const files = typeof Alpine !== 'undefined' && Alpine.reactive
5705
5614
  ? Alpine.reactive([])
5706
5615
  : [];
@@ -5723,8 +5632,7 @@ function createReactiveFileManager(tableName, entryId, bucketName, columnName) {
5723
5632
  throw new Error(`[Manifest Data] Table "${tableName}" not found`);
5724
5633
  }
5725
5634
 
5726
- // CRITICAL: parseStorageConfig uses getManifest() which might return null
5727
- // Instead, parse the storage config directly using the manifest we already have
5635
+ // Parse storage config directly; parseStorageConfig's getManifest() may be null here
5728
5636
  const storageConfigObj = tableDataSource?.storage;
5729
5637
  if (!storageConfigObj || typeof storageConfigObj !== 'object') {
5730
5638
  throw new Error(`[Manifest Data] Storage bucket "${bucketName}" not configured for table "${tableName}"`);
@@ -5773,8 +5681,7 @@ function createReactiveFileManager(tableName, entryId, bucketName, columnName) {
5773
5681
  files.length = 0;
5774
5682
  files.push(...loadedFiles);
5775
5683
 
5776
- // Update lastSeenFileIds to match what was actually loaded
5777
- // This prevents false positives when the watch effect runs
5684
+ // Sync lastSeenFileIds so the watch effect doesn't false-positive
5778
5685
  const $x = window.Alpine?.magic?.('x')?.();
5779
5686
  if ($x && $x[tableName] && Array.isArray($x[tableName])) {
5780
5687
  const entry = $x[tableName].find(item => item.$id === entryId);
@@ -5800,8 +5707,7 @@ function createReactiveFileManager(tableName, entryId, bucketName, columnName) {
5800
5707
  const eventTableName = e.detail?.tableName || 'projects'; // Default to 'projects' for backward compat
5801
5708
 
5802
5709
  if (eventTableName === tableName && eventEntryId === entryId) {
5803
- // Use requestAnimationFrame for immediate execution in next frame
5804
- // This ensures DOM updates are complete but doesn't add unnecessary delay
5710
+ // rAF: run after DOM updates settle
5805
5711
  requestAnimationFrame(() => {
5806
5712
  loadFiles();
5807
5713
  });
@@ -5853,8 +5759,7 @@ function createReactiveFileManager(tableName, entryId, bucketName, columnName) {
5853
5759
  });
5854
5760
  };
5855
5761
 
5856
- // Watch table data for fileIds changes
5857
- // CRITICAL: Track last seen fileIds for THIS specific entry to prevent false positives
5762
+ // Watch table data for fileIds changes; tracked per entry to avoid false positives
5858
5763
  let lastSeenFileIds = null;
5859
5764
 
5860
5765
  const watchTableData = () => {
@@ -5868,15 +5773,13 @@ function createReactiveFileManager(tableName, entryId, bucketName, columnName) {
5868
5773
  const currentFileIds = entry[columnName || 'fileIds'] || [];
5869
5774
  const currentFileIdsStr = JSON.stringify(currentFileIds);
5870
5775
 
5871
- // Only reload if fileIds actually changed for THIS entry
5872
- // Compare with lastSeenFileIds, not with files array (which might be stale)
5776
+ // Reload only when fileIds changed for THIS entry (files array may be stale);
5777
+ // set lastSeenFileIds before loading to prevent duplicate loads
5873
5778
  if (lastSeenFileIds !== currentFileIdsStr && !loading) {
5874
- // Update lastSeenFileIds BEFORE loading to prevent duplicate loads
5875
5779
  lastSeenFileIds = currentFileIdsStr;
5876
5780
 
5877
- // Load files immediately - don't wait for next frame
5878
5781
  loadFiles().then(() => {
5879
- // After loading, verify fileIds still match (in case they changed during load)
5782
+ // Re-verify fileIds in case they changed during load
5880
5783
  const $xAfter = window.Alpine?.magic?.('x')?.();
5881
5784
  if ($xAfter && $xAfter[tableName] && Array.isArray($xAfter[tableName])) {
5882
5785
  const entryAfter = $xAfter[tableName].find(item => item.$id === entryId);
@@ -5889,13 +5792,10 @@ function createReactiveFileManager(tableName, entryId, bucketName, columnName) {
5889
5792
  console.error('[Manifest Data] Failed to load files after fileIds change:', err);
5890
5793
  });
5891
5794
  } else if (lastSeenFileIds === null) {
5892
- // Initialize lastSeenFileIds on first run
5893
5795
  lastSeenFileIds = currentFileIdsStr;
5894
5796
  }
5895
5797
 
5896
- // CRITICAL: Also check if files array is out of sync with fileIds
5897
- // This handles cases where fileIds changed but watch didn't trigger
5898
- // Use the already-declared currentFileIds variable
5798
+ // Sync check: fileIds may have changed without triggering the watch
5899
5799
  const filesFileIds = files.map(f => f.$id);
5900
5800
  const fileIdsMatch = currentFileIds.length === filesFileIds.length &&
5901
5801
  currentFileIds.every(id => filesFileIds.includes(id));
@@ -5989,27 +5889,21 @@ async function getFilesForEntry(tableName, entryId, bucketId, fileIdsColumn = 'f
5989
5889
  return [];
5990
5890
  }
5991
5891
 
5992
- // Get all files from the bucket by calling Appwrite storage directly
5993
- // This bypasses any scope filtering that might be applied in loadDataSource
5994
- // We need ALL files the user has access to, not just those matching the current scope
5892
+ // List bucket files via Appwrite directly — bypasses loadDataSource scope filtering
5995
5893
  const services = await window.ManifestDataAppwrite._getAppwriteDataServices?.();
5996
5894
  if (!services?.storage) {
5997
5895
  throw new Error('[Manifest Data] Appwrite Storage service not available');
5998
5896
  }
5999
5897
 
6000
- // Call Appwrite storage.listFiles directly to get all files user has access to
6001
- // This returns files based on Appwrite's permission system, not our scope filtering
6002
5898
  const response = await services.storage.listFiles(bucketId, []);
6003
5899
  const allFiles = response?.files || [];
6004
5900
 
6005
5901
  // Filter to only files that are in the entry's fileIds array
6006
5902
  let entryFiles = allFiles.filter(file => fileIds.includes(file.$id));
6007
5903
 
6008
- // Check for missing files - these might have been uploaded with incorrect permissions
6009
- // (e.g., before we fixed buildStoragePermissions to use project team permissions)
5904
+ // Missing files: may exist with different permissions, or be deleted
6010
5905
  const missingFileIds = fileIds.filter(id => !entryFiles.some(f => f.$id === id));
6011
5906
 
6012
- // CRITICAL DEBUG: Log missing files to understand where stale fileIds come from
6013
5907
  if (missingFileIds.length > 0) {
6014
5908
  console.warn('[Manifest Data] Found missing fileIds in database entry:', {
6015
5909
  tableName,
@@ -6020,13 +5914,10 @@ async function getFilesForEntry(tableName, entryId, bucketId, fileIdsColumn = 'f
6020
5914
  });
6021
5915
  }
6022
5916
 
6023
- // Note: missingFileIds are silently skipped - they may have been deleted
6024
- // CRITICAL: If we have missing fileIds, we should clean them up from the database
6025
- // instead of trying to fetch them (which causes 404s)
6026
- const confirmedMissingFileIds = []; // Track fileIds confirmed to be deleted (404s)
5917
+ // Stale fileIds get cleaned from the database rather than refetched (avoids 404s)
5918
+ const confirmedMissingFileIds = []; // fileIds confirmed deleted (404s)
6027
5919
 
6028
5920
  if (missingFileIds.length > 0) {
6029
- // Log warning about stale fileIds
6030
5921
  console.warn('[Manifest Data] Found stale fileIds in database entry:', {
6031
5922
  tableName,
6032
5923
  entryId,
@@ -6035,12 +5926,9 @@ async function getFilesForEntry(tableName, entryId, bucketId, fileIdsColumn = 'f
6035
5926
  suggestion: 'These fileIds will be cleaned up automatically'
6036
5927
  });
6037
5928
 
6038
- // Try to fetch missing files individually ONLY if they might exist with different permissions
6039
- // Skip files that are clearly deleted (404s) to avoid unnecessary API calls
5929
+ // Fetch missing files individually; getFile is more reliable than getFileView for existence checks
6040
5930
  for (const missingFileId of missingFileIds) {
6041
5931
  try {
6042
- // Try to get file metadata directly using getFile if available
6043
- // This is more reliable than getFileView for checking if a file exists
6044
5932
  let fileMetadata = null;
6045
5933
  let fileExists = false;
6046
5934
 
@@ -6182,9 +6070,7 @@ async function linkFileToEntry(tableName, entryId, fileId, fileIdsColumn = 'file
6182
6070
  throw new Error(`[Manifest Data] Invalid Appwrite configuration for "${tableName}"`);
6183
6071
  }
6184
6072
 
6185
- // CRITICAL: Read from store FIRST to get the latest optimistic updates
6186
- // This prevents race conditions when multiple files are uploaded concurrently
6187
- // If store has the entry, use it; otherwise fall back to database
6073
+ // Read store first for latest optimistic updates (concurrent uploads race otherwise)
6188
6074
  let fileIds = null;
6189
6075
  const store = typeof Alpine !== 'undefined' && Alpine.store ? Alpine.store('data') : null;
6190
6076
  if (store && store[tableName] && Array.isArray(store[tableName])) {
@@ -6515,8 +6401,7 @@ async function buildStoragePermissions(scope, dataSource = {}) {
6515
6401
  return [];
6516
6402
  }
6517
6403
 
6518
- // Check if file belongs to a database entry (e.g., a project)
6519
- // This allows files to inherit permissions from a table entry
6404
+ // belongsTo: file inherits permissions from a table entry
6520
6405
  if (dataSource.belongsTo) {
6521
6406
  const { table, id } = dataSource.belongsTo;
6522
6407
  if (table && id) {
@@ -9037,16 +8922,12 @@ function registerXMagicMethod(loadDataSource) {
9037
8922
  }
9038
8923
  }
9039
8924
 
9040
- // CRITICAL: Handle base plugin methods ($search, $route, $query) FIRST
9041
- // These are attached directly to arrays by attachArrayMethods
9042
- // Check BEFORE other handlers to ensure they're accessible even if Alpine wraps the array
8925
+ // Base plugin methods first — must resolve even when Alpine wraps the array;
8926
+ // empty-array fallback covers the loading window
9043
8927
  if (key === '$search' || key === '$query') {
9044
- // Check if method exists on target (works even if Alpine wrapped it)
9045
8928
  if (target && typeof target === 'object' && key in target && typeof target[key] === 'function') {
9046
8929
  return target[key].bind(target);
9047
8930
  }
9048
- // Fallback: return safe function that returns empty array
9049
- // This prevents Alpine errors when method doesn't exist yet (during loading)
9050
8931
  return function () {
9051
8932
  return [];
9052
8933
  };
@@ -9054,11 +8935,9 @@ function registerXMagicMethod(loadDataSource) {
9054
8935
 
9055
8936
  // Handle $route function
9056
8937
  if (key === '$route') {
9057
- // First check if it exists as a method on the array
9058
8938
  if (target && typeof target === 'object' && key in target && typeof target[key] === 'function') {
9059
8939
  return target[key].bind(target);
9060
8940
  }
9061
- // Otherwise create the function
9062
8941
  return function (pathKey) {
9063
8942
  if (target && typeof target === 'object') {
9064
8943
  const createRouteProxy = window.ManifestDataProxies?.createRouteProxy;
@@ -9132,18 +9011,14 @@ function registerXMagicMethod(loadDataSource) {
9132
9011
  const createLoadingProxy = window.ManifestDataProxiesCore?.createLoadingProxy;
9133
9012
  return createLoadingProxy ? createLoadingProxy(prop) : {};
9134
9013
  }
9135
- // CRITICAL: Handle ALL array methods - Alpine may access these before other handlers
9136
- // Check if this is ANY array method from Array.prototype (like createArrayProxyWithRoute does)
9014
+ // Array methods bound to target (works even when Alpine proxies the array)
9137
9015
  if (typeof key === 'string' && typeof Array.prototype[key] === 'function') {
9138
- // First try to use the method from the target if it exists and is callable
9139
9016
  if (typeof target[key] === 'function') {
9140
9017
  const bound = target[key].bind(target);
9141
- // Ensure function has proper prototype for Alpine's instanceof checks
9018
+ // Proper Function prototype for Alpine's instanceof checks
9142
9019
  Object.setPrototypeOf(bound, Function.prototype);
9143
9020
  return bound;
9144
9021
  }
9145
- // Fallback: use Array.prototype method bound to target
9146
- // This ensures methods work even if Alpine proxies the array
9147
9022
  const bound = Array.prototype[key].bind(target);
9148
9023
  Object.setPrototypeOf(bound, Function.prototype);
9149
9024
  return bound;
@@ -9156,11 +9031,9 @@ function registerXMagicMethod(loadDataSource) {
9156
9031
  return createLoadingProxy ? createLoadingProxy(prop) : {};
9157
9032
  }
9158
9033
 
9159
- // CRITICAL: If target is frozen, return properties directly without proxying
9160
- // Frozen objects are plain copies returned from nested proxies to prevent recursion
9034
+ // Frozen targets are plain copies from nested proxies — return directly, never proxy
9161
9035
  if (Object.isFrozen(target)) {
9162
9036
  const value = target[key];
9163
- // Return primitives directly, don't proxy anything from frozen objects
9164
9037
  return value;
9165
9038
  }
9166
9039
 
@@ -9169,24 +9042,19 @@ function registerXMagicMethod(loadDataSource) {
9169
9042
 
9170
9043
  if (nestedValue !== undefined && nestedValue !== null) {
9171
9044
  if (Array.isArray(nestedValue)) {
9172
- // CRITICAL: For arrays accessed from dataSourceProxy (like $x.json.products),
9173
- // we need to wrap them in a proxy that handles $route and array methods
9174
- // at the proxy level, just like we do in createNestedObjectProxy
9045
+ // Nested arrays (e.g. $x.json.products) get $route/array methods at the
9046
+ // proxy level, mirroring createNestedObjectProxy
9175
9047
  const attachArrayMethods = window.ManifestDataProxies?.attachArrayMethods;
9176
9048
  let arrayWithMethods = nestedValue;
9177
9049
  if (attachArrayMethods) {
9178
9050
  arrayWithMethods = attachArrayMethods(nestedValue, prop, reloadDataSource);
9179
9051
  }
9180
9052
 
9181
- // Create a proxy that handles $route and array methods at the top level
9182
- // This ensures Alpine's wrapper can see them (like Appwrite methods)
9183
9053
  const createRouteProxy = window.ManifestDataProxies?.createRouteProxy;
9184
9054
  const dataSourceName = prop; // Capture outer prop (data source name like 'json')
9185
9055
  const arrayKey = key; // Capture the array property name (like 'products')
9186
9056
 
9187
- // CRITICAL: Create a Proxy that properly forwards array methods AND Appwrite methods
9188
- // This ensures both work even when Alpine wraps this proxy
9189
- // We use a Proxy with proper get/has/getOwnPropertyDescriptor traps
9057
+ // Forward array methods AND Appwrite methods so both survive Alpine wrapping
9190
9058
  const appwriteMethodNames = ['$create', '$update', '$delete', '$query', '$url', '$download', '$preview', '$openUrl', '$openPreview', '$openDownload', '$filesFor', '$unlinkFrom', '$removeFrom', '$remove'];
9191
9059
  const baseMethodNames = ['$search', '$route', '$query']; // Base plugin methods available for all data sources
9192
9060
 
@@ -9198,8 +9066,7 @@ function registerXMagicMethod(loadDataSource) {
9198
9066
  if (target && typeof target === 'object' && prop in target && typeof target[prop] === 'function') {
9199
9067
  return target[prop].bind(target);
9200
9068
  }
9201
- // Fallback: return safe function that returns empty array
9202
- // This prevents Alpine errors when method doesn't exist yet (during loading)
9069
+ // Empty-array fallback covers the loading window
9203
9070
  if (prop === '$search' || prop === '$query') {
9204
9071
  return function () {
9205
9072
  return [];
@@ -9328,8 +9195,7 @@ function registerXMagicMethod(loadDataSource) {
9328
9195
  }
9329
9196
  // Only create proxy for objects, return primitives directly
9330
9197
  if (typeof nestedValue === 'object' && nestedValue !== null) {
9331
- // CRITICAL: If object is frozen, return it directly without proxying
9332
- // Frozen objects are plain copies returned from nested proxies to prevent recursion
9198
+ // Frozen objects are plain copies from nested proxies — never re-proxy
9333
9199
  if (Object.isFrozen(nestedValue)) {
9334
9200
  return nestedValue;
9335
9201
  }
@@ -9346,10 +9212,7 @@ function registerXMagicMethod(loadDataSource) {
9346
9212
  // Return primitive values directly
9347
9213
  return nestedValue;
9348
9214
  }
9349
- // When nestedValue is undefined/null, return an empty array with methods attached
9350
- // This ensures ($x.example.products || []) works correctly - the array will have $search/$query methods
9351
- // We can't know if it should be an array, but returning an array with methods is safer than undefined
9352
- // because it allows method chaining without errors
9215
+ // Undefined/null: empty array with methods so ($x.example.products || []) chains safely
9353
9216
  const attachArrayMethods = window.ManifestDataProxies?.attachArrayMethods;
9354
9217
  const emptyArray = [];
9355
9218
  if (attachArrayMethods) {
@@ -11632,11 +11495,8 @@ async function initializeDataSourcesPlugin() {
11632
11495
  _currentUrl: existingStore._currentUrl || window.location.pathname
11633
11496
  });
11634
11497
 
11635
- // Pre-load local file-backed sources so $x.* has real data on the first
11636
- // render pass (avoids SPA flash + missing prerender data). At most one
11637
- // fetch each (localized objects load only the current + default locale).
11638
- // Skipped/on-demand: Appwrite (needs auth), API-URL (side-effects), and
11639
- // the special "manifest" key (handled separately below).
11498
+ // Pre-load local file-backed sources so $x.* renders real data first pass;
11499
+ // Appwrite, API-URL, and "manifest" sources stay on-demand.
11640
11500
  try {
11641
11501
  const manifest = await window.ManifestDataConfig.ensureManifest();
11642
11502
  const locale = (typeof document !== 'undefined' && document.documentElement?.lang) || (typeof Alpine !== 'undefined' && Alpine.store('locale')?.current) || 'en';