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.
- package/README.md +21 -3
- package/lib/manifest.accordion.css +0 -3
- package/lib/manifest.appwrite.auth.js +22 -55
- package/lib/manifest.appwrite.data.js +8 -31
- package/lib/manifest.button.css +7 -5
- package/lib/manifest.chart.css +2 -2
- package/lib/manifest.checkbox.css +2 -3
- package/lib/manifest.code.css +38 -39
- package/lib/manifest.code.min.css +1 -1
- package/lib/manifest.colorpicker.css +25 -0
- package/lib/manifest.combobox.css +1 -1
- package/lib/manifest.css +317 -424
- package/lib/manifest.d.ts +3 -3
- package/lib/manifest.data.js +79 -219
- package/lib/manifest.datepicker.css +32 -5
- package/lib/manifest.dialog.css +9 -4
- package/lib/manifest.dropdown.css +57 -40
- package/lib/manifest.form.css +3 -3
- package/lib/manifest.input.css +1 -3
- package/lib/manifest.integrity.json +5 -5
- package/lib/manifest.js +8 -26
- package/lib/manifest.min.css +1 -1
- package/lib/manifest.radio.css +2 -0
- package/lib/manifest.range.css +3 -3
- package/lib/manifest.resize.css +131 -133
- package/lib/manifest.sidebar.css +7 -2
- package/lib/manifest.slides.css +10 -11
- package/lib/manifest.switch.css +1 -1
- package/lib/manifest.theme.css +4 -0
- package/lib/manifest.toast.css +1 -1
- package/lib/manifest.tooltip.css +30 -38
- package/lib/manifest.typography.css +0 -2
- package/lib/manifest.utilities.css +0 -1
- package/lib/manifest.utilities.js +2 -7
- package/package.json +6 -4
package/lib/manifest.data.js
CHANGED
|
@@ -364,10 +364,8 @@ function updateStore(dataSourceName, data, options = {}) {
|
|
|
364
364
|
checkAndDispatchRenderReady();
|
|
365
365
|
}
|
|
366
366
|
|
|
367
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
|
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
|
-
//
|
|
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
|
-
//
|
|
4262
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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;
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
|
4628
|
+
// Fall through (including numeric indices)
|
|
4684
4629
|
return proxyTarget[prop];
|
|
4685
4630
|
},
|
|
4686
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
4842
|
+
// Depth reset handled by the nested proxy
|
|
4927
4843
|
return nestedProxy;
|
|
4928
4844
|
}
|
|
4929
4845
|
|
|
4930
|
-
//
|
|
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
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
5872
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
6024
|
-
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
9041
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
9173
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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.*
|
|
11636
|
-
//
|
|
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';
|