mnfst 0.5.163 → 0.5.165
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/lib/manifest.appwrite.auth.js +107 -199
- package/lib/manifest.appwrite.data.js +1 -3
- package/lib/manifest.appwrite.presences.js +292 -0
- package/lib/manifest.charts.js +34 -79
- package/lib/manifest.chat.js +515 -0
- package/lib/manifest.code.js +110 -340
- package/lib/manifest.colorpicker.js +252 -551
- package/lib/manifest.combobox.js +73 -91
- package/lib/manifest.components.js +34 -95
- package/lib/manifest.css +0 -132
- package/lib/manifest.data.js +189 -562
- package/lib/manifest.datepicker.js +43 -128
- package/lib/manifest.dropdowns.js +18 -39
- package/lib/manifest.export.js +29 -147
- package/lib/manifest.icons.js +9 -24
- package/lib/manifest.integrity.json +24 -23
- package/lib/manifest.js +65 -129
- package/lib/manifest.localization.js +67 -200
- package/lib/manifest.markdown.js +65 -174
- package/lib/manifest.min.css +1 -1
- package/lib/manifest.payments.js +40 -139
- package/lib/manifest.router.js +24 -73
- package/lib/manifest.status.js +20 -56
- package/lib/manifest.svg.js +12 -30
- package/lib/manifest.tooltips.js +28 -65
- package/lib/manifest.utilities.js +40 -107
- package/lib/manifest.virtual.js +15 -53
- package/package.json +1 -1
- package/lib/manifest.appwrite.presence.js +0 -1650
package/lib/manifest.data.js
CHANGED
|
@@ -250,24 +250,18 @@ window.ManifestDataConfig = {
|
|
|
250
250
|
|
|
251
251
|
/* Manifest Data Sources - Store Management */
|
|
252
252
|
|
|
253
|
-
//
|
|
253
|
+
// Raw data, kept out of Alpine's store to avoid double-proxying (recursion)
|
|
254
254
|
const dataSourceCache = new Map();
|
|
255
255
|
const loadingPromises = new Map();
|
|
256
|
-
|
|
257
|
-
// Store raw data separately from Alpine's reactive store
|
|
258
|
-
// This prevents Alpine from proxying our data, which causes recursion when we proxy it
|
|
259
256
|
const rawDataStore = new Map();
|
|
260
257
|
|
|
261
|
-
// Track initialization state
|
|
262
258
|
let isInitializing = false;
|
|
263
259
|
let initializationComplete = false;
|
|
264
260
|
|
|
265
|
-
// Render-ready: debounced timer used by checkAndDispatchRenderReady
|
|
266
261
|
let _renderReadyTimer = null;
|
|
267
|
-
const RENDER_READY_QUIET_MS = 150; //
|
|
262
|
+
const RENDER_READY_QUIET_MS = 150; // quiet time before firing render-ready
|
|
268
263
|
|
|
269
|
-
// Deep seal
|
|
270
|
-
// This prevents double-proxying which causes recursion errors
|
|
264
|
+
// Deep seal so Alpine won't proxy (double-proxying causes recursion)
|
|
271
265
|
function deepSeal(obj) {
|
|
272
266
|
if (obj === null || typeof obj !== 'object') {
|
|
273
267
|
return obj;
|
|
@@ -284,12 +278,8 @@ function deepSeal(obj) {
|
|
|
284
278
|
}
|
|
285
279
|
}
|
|
286
280
|
} else {
|
|
287
|
-
//
|
|
288
|
-
//
|
|
289
|
-
// function" on any object that either lacks the Object prototype
|
|
290
|
-
// (e.g. Object.create(null)) or has a column literally named
|
|
291
|
-
// `hasOwnProperty` shadowing the prototype method — both of which
|
|
292
|
-
// can happen with payloads from Appwrite / arbitrary backends.
|
|
281
|
+
// Object.keys, not for…in + .hasOwnProperty: backend payloads may lack
|
|
282
|
+
// the Object prototype or shadow hasOwnProperty with a column of that name.
|
|
293
283
|
for (const key of Object.keys(obj)) {
|
|
294
284
|
const value = obj[key];
|
|
295
285
|
if (value !== null && typeof value === 'object') {
|
|
@@ -416,15 +406,8 @@ function createReactiveReferences(data, dataSourceName = null) {
|
|
|
416
406
|
}
|
|
417
407
|
|
|
418
408
|
if (typeof data === 'object') {
|
|
419
|
-
//
|
|
420
|
-
//
|
|
421
|
-
// rather than for…in + .hasOwnProperty(). The latter pattern
|
|
422
|
-
// throws "hasOwnProperty is not a function" on any payload that
|
|
423
|
-
// either has a column literally named `hasOwnProperty` shadowing
|
|
424
|
-
// the prototype, or lacks the Object prototype entirely
|
|
425
|
-
// (Object.create(null), some SDK response shapes). This is the
|
|
426
|
-
// hot path for every Appwrite mutation result and realtime event,
|
|
427
|
-
// so it must be defensive about arbitrary backend payloads.
|
|
409
|
+
// Object.keys, not for…in + .hasOwnProperty (see deepSeal). Hot path for
|
|
410
|
+
// every Appwrite mutation result and realtime event.
|
|
428
411
|
const newObj = {};
|
|
429
412
|
for (const key of Object.keys(data)) {
|
|
430
413
|
const value = data[key];
|
|
@@ -841,9 +824,7 @@ function setupTeamChangeListener() {
|
|
|
841
824
|
}
|
|
842
825
|
}
|
|
843
826
|
|
|
844
|
-
// Dispatch manifest:render-ready
|
|
845
|
-
// Uses a debounce so rapid sequential source completions coalesce into one event.
|
|
846
|
-
// The render script listens for this event instead of polling internal store state.
|
|
827
|
+
// Dispatch manifest:render-ready once all sources settle (debounced to coalesce).
|
|
847
828
|
function checkAndDispatchRenderReady() {
|
|
848
829
|
if (_renderReadyTimer) {
|
|
849
830
|
clearTimeout(_renderReadyTimer);
|
|
@@ -2828,23 +2809,9 @@ window.ManifestDataProxies.globalAccessCache = globalAccessCache;
|
|
|
2828
2809
|
|
|
2829
2810
|
|
|
2830
2811
|
/* Manifest Data Sources - Circular Reference Handler */
|
|
2831
|
-
//
|
|
2832
|
-
//
|
|
2833
|
-
|
|
2834
|
-
/**
|
|
2835
|
-
* Handles circular reference detection and resolution
|
|
2836
|
-
* @param {Object} params - Handler parameters
|
|
2837
|
-
* @param {Set} params.activeProps - Set of currently active property accesses
|
|
2838
|
-
* @param {string} params.propKey - The property key being accessed
|
|
2839
|
-
* @param {Object} params.rawTarget - Raw target object (not Alpine-wrapped)
|
|
2840
|
-
* @param {Array} params.path - Path array to the current object
|
|
2841
|
-
* @param {string} params.key - The key being accessed
|
|
2842
|
-
* @param {string} params.fullPath - Full path string for logging
|
|
2843
|
-
* @param {number} params.currentDepth - Current call depth
|
|
2844
|
-
* @param {string} params.triggeredBy - What triggered this access ('Alpine', 'Proxy', etc.)
|
|
2845
|
-
* @param {boolean} params.shouldLog - Whether to log debug information
|
|
2846
|
-
* @returns {*} The resolved value or undefined to break the cycle
|
|
2847
|
-
*/
|
|
2812
|
+
// Breaks infinite recursion when Alpine re-evaluates an expression that
|
|
2813
|
+
// re-reads a property still being accessed. Returns the cached plain copy if
|
|
2814
|
+
// available, else undefined to break the cycle.
|
|
2848
2815
|
function handleCircularReference({
|
|
2849
2816
|
activeProps,
|
|
2850
2817
|
propKey,
|
|
@@ -2857,18 +2824,16 @@ function handleCircularReference({
|
|
|
2857
2824
|
shouldLog
|
|
2858
2825
|
}) {
|
|
2859
2826
|
if (!activeProps || !activeProps.has(propKey)) {
|
|
2860
|
-
return null; // Not
|
|
2827
|
+
return null; // Not circular, continue normal flow
|
|
2861
2828
|
}
|
|
2862
2829
|
|
|
2863
2830
|
if (shouldLog) {
|
|
2864
2831
|
console.warn(`[Proxy] ⚠️ CIRCULAR ${fullPath} | depth:${currentDepth} | triggered by:${triggeredBy} | This is likely Alpine re-evaluation`);
|
|
2865
2832
|
}
|
|
2866
2833
|
|
|
2867
|
-
//
|
|
2868
|
-
//
|
|
2869
|
-
// if it exists. This prevents infinite recursion by ensuring Alpine gets the same object instance.
|
|
2834
|
+
// Prop already in flight (Alpine re-evaluating): hand back the cached plain
|
|
2835
|
+
// copy so Alpine gets the same instance and doesn't recurse.
|
|
2870
2836
|
try {
|
|
2871
|
-
// First, try to get the value from rawTarget
|
|
2872
2837
|
let current = rawTarget;
|
|
2873
2838
|
let pathValid = true;
|
|
2874
2839
|
const accessPath = path.length === 0 ? [key] : [...path, key];
|
|
@@ -2892,7 +2857,7 @@ function handleCircularReference({
|
|
|
2892
2857
|
return current;
|
|
2893
2858
|
}
|
|
2894
2859
|
|
|
2895
|
-
//
|
|
2860
|
+
// Simple object: return its cached plain copy if present
|
|
2896
2861
|
if (!Array.isArray(current)) {
|
|
2897
2862
|
let isSimpleObject = true;
|
|
2898
2863
|
try {
|
|
@@ -2907,7 +2872,6 @@ function handleCircularReference({
|
|
|
2907
2872
|
}
|
|
2908
2873
|
|
|
2909
2874
|
if (isSimpleObject) {
|
|
2910
|
-
// Check for cached plain copy first - this is critical to prevent recursion
|
|
2911
2875
|
if (!window.ManifestDataProxiesCore.frozenPlainCopyCache) {
|
|
2912
2876
|
window.ManifestDataProxiesCore.frozenPlainCopyCache = new WeakMap();
|
|
2913
2877
|
}
|
|
@@ -2915,8 +2879,7 @@ function handleCircularReference({
|
|
|
2915
2879
|
const cachedCopy = plainCopyCache.get(current);
|
|
2916
2880
|
|
|
2917
2881
|
if (cachedCopy) {
|
|
2918
|
-
//
|
|
2919
|
-
// DON'T remove from activeProps here - let the normal flow handle it
|
|
2882
|
+
// Leave propKey in activeProps — normal flow clears it
|
|
2920
2883
|
return cachedCopy;
|
|
2921
2884
|
}
|
|
2922
2885
|
}
|
|
@@ -2948,14 +2911,10 @@ if (typeof window !== 'undefined') {
|
|
|
2948
2911
|
|
|
2949
2912
|
|
|
2950
2913
|
/* Manifest Data Sources - Simple Object Handler */
|
|
2951
|
-
//
|
|
2952
|
-
//
|
|
2914
|
+
// Plain copies of primitive-only objects break the proxy chain so Alpine
|
|
2915
|
+
// wrapping + nested access can't recurse infinitely.
|
|
2953
2916
|
|
|
2954
|
-
|
|
2955
|
-
* Checks if an object is a "simple object" (contains only primitives, no nested objects/arrays)
|
|
2956
|
-
* @param {*} value - The value to check
|
|
2957
|
-
* @returns {boolean} True if the object is simple (only primitives)
|
|
2958
|
-
*/
|
|
2917
|
+
// True if value is an object of only primitives (no nested objects/arrays)
|
|
2959
2918
|
function isSimpleObject(value) {
|
|
2960
2919
|
if (!value || typeof value !== 'object' || Array.isArray(value) || value === null) {
|
|
2961
2920
|
return false;
|
|
@@ -2973,19 +2932,8 @@ function isSimpleObject(value) {
|
|
|
2973
2932
|
}
|
|
2974
2933
|
}
|
|
2975
2934
|
|
|
2976
|
-
|
|
2977
|
-
|
|
2978
|
-
* Plain copies are NOT frozen - Alpine needs to access properties on them
|
|
2979
|
-
* @param {Object} value - The simple object to copy
|
|
2980
|
-
* @param {Object} params - Handler parameters
|
|
2981
|
-
* @param {Set} params.activeProps - Set of currently active property accesses
|
|
2982
|
-
* @param {string} params.propKey - The property key being accessed
|
|
2983
|
-
* @param {Object} params.rawTarget - Raw target object
|
|
2984
|
-
* @param {string} params.fullPath - Full path string for logging
|
|
2985
|
-
* @param {Map} params.callDepthMap - Map tracking call depth
|
|
2986
|
-
* @param {boolean} params.shouldLog - Whether to log debug information
|
|
2987
|
-
* @returns {Object|null} The plain copy, or null if not a simple object or copy failed
|
|
2988
|
-
*/
|
|
2935
|
+
// Get/create a cached plain copy of a simple object. Not frozen — Alpine
|
|
2936
|
+
// reads properties off it. Returns null if not simple or copy failed.
|
|
2989
2937
|
function createOrGetPlainCopy(value, {
|
|
2990
2938
|
activeProps,
|
|
2991
2939
|
propKey,
|
|
@@ -3004,62 +2952,41 @@ function createOrGetPlainCopy(value, {
|
|
|
3004
2952
|
}
|
|
3005
2953
|
const plainCopyCache = window.ManifestDataProxiesCore.frozenPlainCopyCache;
|
|
3006
2954
|
|
|
3007
|
-
//
|
|
2955
|
+
// Cached copy = same instance, so Alpine won't re-evaluate (prevents recursion)
|
|
3008
2956
|
let cachedCopy = plainCopyCache.get(value);
|
|
3009
2957
|
if (cachedCopy) {
|
|
3010
|
-
// Return cached plain copy - Alpine won't see it as "new"
|
|
3011
|
-
// CRITICAL: Remove from activeProps since plain copy is plain object (won't trigger proxy getters)
|
|
3012
2958
|
if (activeProps) {
|
|
3013
2959
|
activeProps.delete(propKey);
|
|
3014
2960
|
}
|
|
3015
|
-
// Reset depth after returning plain copy
|
|
3016
2961
|
if (callDepthMap && rawTarget) {
|
|
3017
2962
|
callDepthMap.delete(rawTarget);
|
|
3018
2963
|
}
|
|
3019
2964
|
return cachedCopy;
|
|
3020
2965
|
}
|
|
3021
2966
|
|
|
3022
|
-
// Create new plain copy
|
|
3023
|
-
|
|
3024
2967
|
const plainCopy = {};
|
|
3025
2968
|
try {
|
|
3026
|
-
// CRITICAL: Copy property values directly (they're already primitives for simple objects)
|
|
3027
|
-
// Don't freeze nested values - just copy them as-is
|
|
3028
2969
|
for (const prop in value) {
|
|
3029
2970
|
plainCopy[prop] = value[prop];
|
|
3030
2971
|
}
|
|
3031
2972
|
|
|
3032
|
-
// Don't freeze the object - Alpine needs to access properties on it
|
|
3033
|
-
// Instead, return a plain object copy which Alpine won't wrap in reactivity
|
|
3034
|
-
// because it's a new object instance each time (cached by WeakMap)
|
|
3035
|
-
|
|
3036
|
-
// Cache the plain copy for future accesses (same instance = Alpine won't re-evaluate)
|
|
3037
2973
|
plainCopyCache.set(value, plainCopy);
|
|
3038
2974
|
|
|
3039
|
-
//
|
|
3040
|
-
//
|
|
3041
|
-
// This prevents false circular reference detection when Alpine accesses properties on the plain copy
|
|
2975
|
+
// Plain copy breaks the proxy chain, so drop it from activeProps to
|
|
2976
|
+
// avoid false circular-reference detection on Alpine's follow-up reads.
|
|
3042
2977
|
if (activeProps) {
|
|
3043
2978
|
activeProps.delete(propKey);
|
|
3044
2979
|
}
|
|
3045
|
-
// Reset depth after returning plain copy
|
|
3046
2980
|
if (callDepthMap && rawTarget) {
|
|
3047
2981
|
callDepthMap.delete(rawTarget);
|
|
3048
2982
|
}
|
|
3049
2983
|
return plainCopy;
|
|
3050
2984
|
} catch (e) {
|
|
3051
|
-
// Return null to indicate failure - caller should fall through to proxy creation
|
|
3052
2985
|
return null;
|
|
3053
2986
|
}
|
|
3054
2987
|
}
|
|
3055
2988
|
|
|
3056
|
-
|
|
3057
|
-
* Handles simple object detection and plain copy creation for a value
|
|
3058
|
-
* This is the main entry point for simple object handling
|
|
3059
|
-
* @param {*} value - The value to check
|
|
3060
|
-
* @param {Object} params - Handler parameters
|
|
3061
|
-
* @returns {Object|null} The plain copy if simple object, null otherwise
|
|
3062
|
-
*/
|
|
2989
|
+
// Entry point: plain copy if value is a simple object, else null
|
|
3063
2990
|
function handleSimpleObject(value, params) {
|
|
3064
2991
|
if (Array.isArray(value)) {
|
|
3065
2992
|
return null; // Arrays are not simple objects
|
|
@@ -3084,15 +3011,8 @@ if (typeof window !== 'undefined') {
|
|
|
3084
3011
|
|
|
3085
3012
|
|
|
3086
3013
|
/* Manifest Data Sources - Proxy Helper Functions */
|
|
3087
|
-
// Utility functions for proxy creation and data manipulation
|
|
3088
3014
|
|
|
3089
|
-
|
|
3090
|
-
* Find an item in nested data structures by path key and segments
|
|
3091
|
-
* @param {*} data - The data to search (array or object)
|
|
3092
|
-
* @param {string} pathKey - The key that contains the path value
|
|
3093
|
-
* @param {Array} pathSegments - Array of path segments to match
|
|
3094
|
-
* @returns {*} The found item or null
|
|
3095
|
-
*/
|
|
3015
|
+
// Find a nested item whose pathKey value matches one of pathSegments
|
|
3096
3016
|
function findItemByPath(data, pathKey, pathSegments) {
|
|
3097
3017
|
if (!pathSegments || pathSegments.length === 0) {
|
|
3098
3018
|
return null;
|
|
@@ -3145,12 +3065,7 @@ function findItemByPath(data, pathKey, pathSegments) {
|
|
|
3145
3065
|
return null;
|
|
3146
3066
|
}
|
|
3147
3067
|
|
|
3148
|
-
|
|
3149
|
-
* Find the group that contains a specific item
|
|
3150
|
-
* @param {*} data - The data to search
|
|
3151
|
-
* @param {*} targetItem - The item to find
|
|
3152
|
-
* @returns {*} The group containing the item or null
|
|
3153
|
-
*/
|
|
3068
|
+
// Find the group (item with .group + .items array) that contains targetItem
|
|
3154
3069
|
function findGroupContainingItem(data, targetItem) {
|
|
3155
3070
|
if (Array.isArray(data)) {
|
|
3156
3071
|
for (const item of data) {
|
|
@@ -3178,11 +3093,7 @@ function findGroupContainingItem(data, targetItem) {
|
|
|
3178
3093
|
return null;
|
|
3179
3094
|
}
|
|
3180
3095
|
|
|
3181
|
-
|
|
3182
|
-
* Convert Alpine proxy to real array
|
|
3183
|
-
* @param {*} proxyData - The proxy data to convert
|
|
3184
|
-
* @returns {Array} The converted array or original value
|
|
3185
|
-
*/
|
|
3096
|
+
// Convert an Alpine proxy (or array-like) to a real array
|
|
3186
3097
|
function convertProxyToArray(proxyData) {
|
|
3187
3098
|
if (Array.isArray(proxyData)) {
|
|
3188
3099
|
return proxyData;
|
|
@@ -3240,8 +3151,7 @@ function clearArrayProxyCacheForDataSource(dataSourceName) {
|
|
|
3240
3151
|
// Track which arrays have methods attached to avoid re-attaching
|
|
3241
3152
|
const arraysWithMethodsAttached = new WeakSet();
|
|
3242
3153
|
|
|
3243
|
-
// Attach methods directly to
|
|
3244
|
-
// This allows Alpine to track the array directly for reactivity
|
|
3154
|
+
// Attach methods directly to the array (no proxy wrapper) so Alpine tracks it directly
|
|
3245
3155
|
function attachArrayMethods(array, dataSourceName, reloadDataSource) {
|
|
3246
3156
|
// Skip if already has methods attached
|
|
3247
3157
|
if (arraysWithMethodsAttached.has(array)) {
|
|
@@ -3263,8 +3173,7 @@ function attachArrayMethods(array, dataSourceName, reloadDataSource) {
|
|
|
3263
3173
|
// Mark as having methods attached
|
|
3264
3174
|
arraysWithMethodsAttached.add(array);
|
|
3265
3175
|
|
|
3266
|
-
//
|
|
3267
|
-
// These need to be accessible on the array for Alpine expressions like $x.assets.$ready
|
|
3176
|
+
// State getters ($loading/$error/$ready) so $x.assets.$ready etc. resolve on the array
|
|
3268
3177
|
Object.defineProperty(array, '$loading', {
|
|
3269
3178
|
enumerable: false,
|
|
3270
3179
|
configurable: true,
|
|
@@ -3323,8 +3232,7 @@ function attachArrayMethods(array, dataSourceName, reloadDataSource) {
|
|
|
3323
3232
|
});
|
|
3324
3233
|
});
|
|
3325
3234
|
|
|
3326
|
-
//
|
|
3327
|
-
// This ensures the returned array has $query, $route, and other methods available
|
|
3235
|
+
// Re-attach so chaining works (.$search().$query())
|
|
3328
3236
|
attachArrayMethods(filtered, dataSourceName, reloadDataSource);
|
|
3329
3237
|
|
|
3330
3238
|
return filtered;
|
|
@@ -3344,11 +3252,10 @@ function attachArrayMethods(array, dataSourceName, reloadDataSource) {
|
|
|
3344
3252
|
});
|
|
3345
3253
|
}
|
|
3346
3254
|
if (array && typeof array === 'object') {
|
|
3347
|
-
//
|
|
3255
|
+
// Prefer raw data — the real array, not the Alpine proxy
|
|
3348
3256
|
const getRawData = window.ManifestDataStore?.getRawData;
|
|
3349
3257
|
let dataToUse = array;
|
|
3350
3258
|
|
|
3351
|
-
// CRITICAL: Always try to get raw data first - this ensures we have the real array
|
|
3352
3259
|
if (dataSourceName && getRawData) {
|
|
3353
3260
|
const rawData = getRawData(dataSourceName);
|
|
3354
3261
|
if (rawData && (Array.isArray(rawData) || (rawData.length !== undefined && rawData.length >= 0))) {
|
|
@@ -3423,23 +3330,11 @@ function attachArrayMethods(array, dataSourceName, reloadDataSource) {
|
|
|
3423
3330
|
}
|
|
3424
3331
|
}
|
|
3425
3332
|
|
|
3426
|
-
//
|
|
3427
|
-
//
|
|
3428
|
-
//
|
|
3429
|
-
//
|
|
3430
|
-
//
|
|
3431
|
-
// a Promise, and `Promise.data` is undefined, so the Appwrite-detection
|
|
3432
|
-
// block silently fell through and isAppwriteSource stayed `false` for
|
|
3433
|
-
// EVERY source — including real Appwrite collections. That caused the
|
|
3434
|
-
// client-side $query to be attached to Appwrite arrays. The client-side
|
|
3435
|
-
// $query sorts/filters in-memory and returns the result (without mutating
|
|
3436
|
-
// the source), while demo code calls $query as a fire-and-forget store
|
|
3437
|
-
// mutation. Result: sort buttons silently no-op'd.
|
|
3438
|
-
//
|
|
3439
|
-
// window.__manifestLoaded and window.ManifestComponentsRegistry.manifest
|
|
3440
|
-
// are populated synchronously by the loader after the manifest fetch
|
|
3441
|
-
// resolves (see manifest.js loader: `window.__manifestLoaded = manifest`).
|
|
3442
|
-
// By the time any data source is being attached, they're available.
|
|
3333
|
+
// Client-side $query, overridden by Appwrite's for Appwrite sources.
|
|
3334
|
+
// Must use the SYNCHRONOUS manifest accessor — ensureManifest() is async,
|
|
3335
|
+
// so `.data` on its unawaited Promise is undefined and detection would
|
|
3336
|
+
// silently fall through, wrongly attaching client-side $query to Appwrite
|
|
3337
|
+
// arrays (in-memory sort discarded → sort buttons no-op).
|
|
3443
3338
|
let isAppwriteSource = false;
|
|
3444
3339
|
if (dataSourceName) {
|
|
3445
3340
|
try {
|
|
@@ -3455,8 +3350,7 @@ function attachArrayMethods(array, dataSourceName, reloadDataSource) {
|
|
|
3455
3350
|
}
|
|
3456
3351
|
}
|
|
3457
3352
|
|
|
3458
|
-
//
|
|
3459
|
-
// Appwrite sources will get their $query from the Appwrite plugin (attached later)
|
|
3353
|
+
// Appwrite sources get their $query from the Appwrite plugin instead
|
|
3460
3354
|
if (!isAppwriteSource && !array.hasOwnProperty('$query')) {
|
|
3461
3355
|
Object.defineProperty(array, '$query', {
|
|
3462
3356
|
enumerable: false,
|
|
@@ -3560,8 +3454,7 @@ function attachArrayMethods(array, dataSourceName, reloadDataSource) {
|
|
|
3560
3454
|
}
|
|
3561
3455
|
}
|
|
3562
3456
|
|
|
3563
|
-
//
|
|
3564
|
-
// This ensures the returned array has $search, $route, and other methods available
|
|
3457
|
+
// Re-attach so chaining works (.$query().$search())
|
|
3565
3458
|
attachArrayMethods(result, dataSourceName, reloadDataSource);
|
|
3566
3459
|
|
|
3567
3460
|
return result;
|
|
@@ -3571,12 +3464,7 @@ function attachArrayMethods(array, dataSourceName, reloadDataSource) {
|
|
|
3571
3464
|
|
|
3572
3465
|
// Attach Appwrite methods ($create, $update, $delete, etc.)
|
|
3573
3466
|
if (dataSourceName) {
|
|
3574
|
-
//
|
|
3575
|
-
// the block above — ensureManifest() is async and returned a Promise
|
|
3576
|
-
// here too, so isAppwriteSource was always false and the Appwrite
|
|
3577
|
-
// $query was never attached, leaving sort/query buttons to silently
|
|
3578
|
-
// fall through to the client-side $query that returns a discarded
|
|
3579
|
-
// sorted array.
|
|
3467
|
+
// Sync manifest accessor again — see note above on the async pitfall.
|
|
3580
3468
|
let isAppwriteSource = false;
|
|
3581
3469
|
try {
|
|
3582
3470
|
const manifest = window.__manifestLoaded
|
|
@@ -3593,7 +3481,7 @@ function attachArrayMethods(array, dataSourceName, reloadDataSource) {
|
|
|
3593
3481
|
const createAppwriteMethodsHandler = window.ManifestDataProxiesAppwrite?.createAppwriteMethodsHandler;
|
|
3594
3482
|
if (createAppwriteMethodsHandler) {
|
|
3595
3483
|
const methodsHandler = createAppwriteMethodsHandler(dataSourceName, reloadDataSource);
|
|
3596
|
-
//
|
|
3484
|
+
// $query only for Appwrite sources; local sources keep the base plugin's
|
|
3597
3485
|
const appwriteMethods = isAppwriteSource
|
|
3598
3486
|
? ['$create', '$update', '$delete', '$duplicate', '$query', '$url', '$download', '$preview', '$openUrl', '$openPreview', '$openDownload', '$filesFor', '$unlinkFrom', '$removeFrom', '$remove']
|
|
3599
3487
|
: ['$create', '$update', '$delete', '$duplicate', '$url', '$download', '$preview', '$openUrl', '$openPreview', '$openDownload', '$filesFor', '$unlinkFrom', '$removeFrom', '$remove'];
|
|
@@ -3704,11 +3592,8 @@ if (typeof window !== 'undefined') {
|
|
|
3704
3592
|
}
|
|
3705
3593
|
|
|
3706
3594
|
function createArrayProxyWithRoute(arrayTarget, dataSourceName = null, reloadDataSource = null) {
|
|
3707
|
-
//
|
|
3708
|
-
// When dataSourceName is null, use WeakMap (original behavior)
|
|
3595
|
+
// With dataSourceName: Map keyed by (array id + name). Without: WeakMap by array.
|
|
3709
3596
|
if (dataSourceName) {
|
|
3710
|
-
// Create a composite key using array identity and dataSourceName
|
|
3711
|
-
// Use a WeakMap to store a unique ID for each array, then use that ID + dataSourceName in Map
|
|
3712
3597
|
if (!window._arrayProxyIdMap) {
|
|
3713
3598
|
window._arrayProxyIdMap = new WeakMap();
|
|
3714
3599
|
window._arrayProxyIdCounter = 0;
|
|
@@ -3731,14 +3616,11 @@ function createArrayProxyWithRoute(arrayTarget, dataSourceName = null, reloadDat
|
|
|
3731
3616
|
}
|
|
3732
3617
|
}
|
|
3733
3618
|
|
|
3734
|
-
// Attach array methods
|
|
3619
|
+
// Attach array methods to the target before proxying (safety net if Alpine reads them directly)
|
|
3735
3620
|
if (Array.isArray(arrayTarget) && !arraysWithMethodsAttached.has(arrayTarget)) {
|
|
3736
|
-
// Attach all standard array methods directly to the array
|
|
3737
|
-
// This is a safety net in case Alpine accesses methods directly on the array
|
|
3738
3621
|
const attachedMethods = [];
|
|
3739
3622
|
ARRAY_METHODS.forEach(methodName => {
|
|
3740
3623
|
if (!(methodName in arrayTarget) || typeof arrayTarget[methodName] !== 'function') {
|
|
3741
|
-
// Only attach if not already present (to avoid overwriting)
|
|
3742
3624
|
try {
|
|
3743
3625
|
Object.defineProperty(arrayTarget, methodName, {
|
|
3744
3626
|
enumerable: false,
|
|
@@ -3758,15 +3640,14 @@ function createArrayProxyWithRoute(arrayTarget, dataSourceName = null, reloadDat
|
|
|
3758
3640
|
} else {
|
|
3759
3641
|
}
|
|
3760
3642
|
|
|
3761
|
-
// Pre-create $files function
|
|
3762
|
-
// Always create it, even if manifest isn't loaded yet - the function will handle it internally
|
|
3643
|
+
// Pre-create $files (function validates table-vs-bucket at call time)
|
|
3763
3644
|
if (dataSourceName) {
|
|
3764
3645
|
const createFilesMethod = window.ManifestDataProxiesMagic?.createFilesMethod;
|
|
3765
3646
|
if (createFilesMethod && !arrayTarget._$filesFunction) {
|
|
3766
3647
|
arrayTarget._$filesFunction = createFilesMethod(dataSourceName);
|
|
3767
3648
|
Object.defineProperty(arrayTarget._$filesFunction, 'name', { value: '$files', configurable: true });
|
|
3768
3649
|
Object.setPrototypeOf(arrayTarget._$filesFunction, Function.prototype);
|
|
3769
|
-
// Also define
|
|
3650
|
+
// Also define on the target so it's reachable without the proxy
|
|
3770
3651
|
Object.defineProperty(arrayTarget, '$files', {
|
|
3771
3652
|
enumerable: true,
|
|
3772
3653
|
configurable: true,
|
|
@@ -3776,9 +3657,7 @@ function createArrayProxyWithRoute(arrayTarget, dataSourceName = null, reloadDat
|
|
|
3776
3657
|
}
|
|
3777
3658
|
}
|
|
3778
3659
|
|
|
3779
|
-
// Attach $route
|
|
3780
|
-
// This ensures $route() works even if Alpine proxies the proxy
|
|
3781
|
-
// Always attach it, even if createRouteProxy isn't available yet (it will be checked at call time)
|
|
3660
|
+
// Attach $route to the target too, so it survives Alpine proxying the proxy
|
|
3782
3661
|
if (!arrayTarget.hasOwnProperty('$route')) {
|
|
3783
3662
|
Object.defineProperty(arrayTarget, '$route', {
|
|
3784
3663
|
enumerable: false,
|
|
@@ -3792,11 +3671,10 @@ function createArrayProxyWithRoute(arrayTarget, dataSourceName = null, reloadDat
|
|
|
3792
3671
|
});
|
|
3793
3672
|
}
|
|
3794
3673
|
if (arrayTarget && typeof arrayTarget === 'object') {
|
|
3795
|
-
//
|
|
3674
|
+
// Prefer raw data — the real array, not the Alpine proxy
|
|
3796
3675
|
const getRawData = window.ManifestDataStore?.getRawData;
|
|
3797
3676
|
let dataToUse = arrayTarget;
|
|
3798
3677
|
|
|
3799
|
-
// CRITICAL: Always try to get raw data first - this ensures we have the real array
|
|
3800
3678
|
if (dataSourceName && getRawData) {
|
|
3801
3679
|
const rawData = getRawData(dataSourceName);
|
|
3802
3680
|
if (rawData && (Array.isArray(rawData) || (rawData.length !== undefined && rawData.length >= 0))) {
|
|
@@ -3804,7 +3682,7 @@ function createArrayProxyWithRoute(arrayTarget, dataSourceName = null, reloadDat
|
|
|
3804
3682
|
}
|
|
3805
3683
|
}
|
|
3806
3684
|
|
|
3807
|
-
//
|
|
3685
|
+
// Otherwise coerce the array-like proxy into a real array
|
|
3808
3686
|
if (!Array.isArray(dataToUse) && dataToUse && typeof dataToUse === 'object' && 'length' in dataToUse) {
|
|
3809
3687
|
try {
|
|
3810
3688
|
dataToUse = Array.from(dataToUse);
|
|
@@ -3830,17 +3708,16 @@ function createArrayProxyWithRoute(arrayTarget, dataSourceName = null, reloadDat
|
|
|
3830
3708
|
});
|
|
3831
3709
|
}
|
|
3832
3710
|
|
|
3833
|
-
//
|
|
3711
|
+
// Base array proxy
|
|
3834
3712
|
const baseProxy = Object.setPrototypeOf(
|
|
3835
3713
|
new Proxy(arrayTarget, {
|
|
3836
3714
|
get(target, key, receiver) {
|
|
3837
3715
|
|
|
3838
|
-
// Handle special keys - but allow Symbol.iterator for array iteration
|
|
3839
3716
|
if (key === 'then' || key === 'catch' || key === 'finally') {
|
|
3840
3717
|
return undefined;
|
|
3841
3718
|
}
|
|
3842
3719
|
|
|
3843
|
-
//
|
|
3720
|
+
// Symbol.iterator needed for Alpine's x-for
|
|
3844
3721
|
if (key === Symbol.iterator) {
|
|
3845
3722
|
const manifest = window.ManifestDataConfig?.getManifest?.();
|
|
3846
3723
|
const ds = manifest?.data?.[dataSourceName];
|
|
@@ -3848,7 +3725,7 @@ function createArrayProxyWithRoute(arrayTarget, dataSourceName = null, reloadDat
|
|
|
3848
3725
|
if (isStorageBucket) {
|
|
3849
3726
|
}
|
|
3850
3727
|
const iterator = target[Symbol.iterator].bind(target);
|
|
3851
|
-
//
|
|
3728
|
+
// Restore Function.prototype for Alpine's instanceof checks
|
|
3852
3729
|
if (iterator && typeof iterator === 'function') {
|
|
3853
3730
|
Object.setPrototypeOf(iterator, Function.prototype);
|
|
3854
3731
|
}
|
|
@@ -3872,14 +3749,11 @@ function createArrayProxyWithRoute(arrayTarget, dataSourceName = null, reloadDat
|
|
|
3872
3749
|
return null;
|
|
3873
3750
|
}
|
|
3874
3751
|
|
|
3875
|
-
//
|
|
3876
|
-
// Also check if it's attached directly to the target (for nested arrays)
|
|
3752
|
+
// $route — prefer one attached to the target (nested arrays)
|
|
3877
3753
|
if (key === '$route') {
|
|
3878
|
-
// First check if it's attached directly to the target
|
|
3879
3754
|
if (target.$route && typeof target.$route === 'function') {
|
|
3880
3755
|
return target.$route;
|
|
3881
3756
|
}
|
|
3882
|
-
// Otherwise, return the function from the proxy handler
|
|
3883
3757
|
const createRouteProxy = window.ManifestDataProxies?.createRouteProxy;
|
|
3884
3758
|
if (!createRouteProxy) {
|
|
3885
3759
|
// Return a function that returns a safe proxy (not a proxy directly)
|
|
@@ -4086,9 +3960,7 @@ function createArrayProxyWithRoute(arrayTarget, dataSourceName = null, reloadDat
|
|
|
4086
3960
|
return undefined;
|
|
4087
3961
|
}
|
|
4088
3962
|
|
|
4089
|
-
//
|
|
4090
|
-
// These methods are called on the array itself (e.g., $x.assets.$create(file))
|
|
4091
|
-
// Only available if Appwrite plugin is loaded
|
|
3963
|
+
// Appwrite methods called on the array itself (e.g. $x.assets.$create(file))
|
|
4092
3964
|
if (dataSourceName && (key === '$create' || key === '$update' || key === '$delete' || key === '$duplicate' || key === '$query' ||
|
|
4093
3965
|
key === '$url' || key === '$download' || key === '$preview' || key === '$filesFor' || key === '$unlinkFrom' || key === '$removeFrom' || key === '$remove')) {
|
|
4094
3966
|
// Check if Appwrite methods handler is available
|
|
@@ -4310,9 +4182,8 @@ function createArrayProxyWithRoute(arrayTarget, dataSourceName = null, reloadDat
|
|
|
4310
4182
|
});
|
|
4311
4183
|
}
|
|
4312
4184
|
|
|
4313
|
-
// Cache
|
|
4314
|
-
//
|
|
4315
|
-
// SKIP CACHE for 'projects' to ensure Alpine gets fresh proxy reference for reactivity
|
|
4185
|
+
// Cache before returning (get handler may recurse). Skip 'projects' so
|
|
4186
|
+
// Alpine always gets a fresh proxy reference for reactivity.
|
|
4316
4187
|
if (dataSourceName && dataSourceName !== 'projects') {
|
|
4317
4188
|
// Use Map cache with composite key
|
|
4318
4189
|
if (!window._arrayProxyIdMap) {
|
|
@@ -6734,37 +6605,18 @@ window.ManifestDataProxiesFiles.buildStoragePermissions = buildStoragePermission
|
|
|
6734
6605
|
window.ManifestDataProxiesFiles.reactiveFileManagers = reactiveFileManagers; // Legacy - will be removed
|
|
6735
6606
|
|
|
6736
6607
|
/* Manifest Data Sources - Route & Proxy Coordinator */
|
|
6737
|
-
//
|
|
6738
|
-
//
|
|
6739
|
-
// - proxies/creation/manifest.data.proxies.helpers.js (helper functions)
|
|
6740
|
-
// - proxies/creation/manifest.data.proxies.array.js (array proxy creation)
|
|
6741
|
-
// - proxies/creation/manifest.data.proxies.object.js (object proxy creation)
|
|
6742
|
-
// - proxies/creation/manifest.data.proxies.route.js (route proxy creation)
|
|
6743
|
-
|
|
6744
|
-
// Re-export functions from proxy creation modules for backward compatibility
|
|
6745
|
-
// These modules export to window.ManifestDataProxies, so we just ensure the namespace exists
|
|
6608
|
+
// Proxy creation lives in proxies/creation/*; those modules self-export to
|
|
6609
|
+
// window.ManifestDataProxies. This file only ensures the namespace exists.
|
|
6746
6610
|
if (typeof window !== 'undefined') {
|
|
6747
6611
|
if (!window.ManifestDataProxies) {
|
|
6748
6612
|
window.ManifestDataProxies = {};
|
|
6749
6613
|
}
|
|
6750
|
-
|
|
6751
|
-
// Functions are already exported by the individual modules:
|
|
6752
|
-
// - createArrayProxyWithRoute (from array.js)
|
|
6753
|
-
// - createRouteProxy (from route.js)
|
|
6754
|
-
// - createNestedObjectProxy (from object.js)
|
|
6755
|
-
// - clearRouteProxyCacheForDataSource (from route.js)
|
|
6756
|
-
// - clearArrayProxyCacheForDataSource (from array.js)
|
|
6757
|
-
// - attachArrayMethods (from array.js)
|
|
6758
|
-
|
|
6759
|
-
// This file serves as a coordinator and ensures all modules are loaded
|
|
6760
|
-
// The build system includes these files in the correct order before this file
|
|
6761
6614
|
}
|
|
6762
6615
|
|
|
6763
6616
|
|
|
6764
6617
|
/* Manifest Data Sources - Appwrite Methods Handler */
|
|
6765
|
-
//
|
|
6618
|
+
// CRUD/storage methods handler for Appwrite tables and buckets
|
|
6766
6619
|
function createAppwriteMethodsHandler(dataSourceName, reloadDataSource) {
|
|
6767
|
-
// Helper to set error state automatically
|
|
6768
6620
|
const setErrorState = (error) => {
|
|
6769
6621
|
const store = Alpine.store('data');
|
|
6770
6622
|
if (store) {
|
|
@@ -6780,11 +6632,9 @@ function createAppwriteMethodsHandler(dataSourceName, reloadDataSource) {
|
|
|
6780
6632
|
};
|
|
6781
6633
|
Alpine.store('data', updatedStore);
|
|
6782
6634
|
}
|
|
6783
|
-
// For test purposes, also log to console
|
|
6784
6635
|
console.error(`[Manifest Data] ${dataSourceName} operation failed:`, error);
|
|
6785
6636
|
};
|
|
6786
6637
|
|
|
6787
|
-
// Helper to clear error state
|
|
6788
6638
|
const clearErrorState = () => {
|
|
6789
6639
|
const store = Alpine.store('data');
|
|
6790
6640
|
if (store) {
|
|
@@ -6804,13 +6654,11 @@ function createAppwriteMethodsHandler(dataSourceName, reloadDataSource) {
|
|
|
6804
6654
|
}
|
|
6805
6655
|
};
|
|
6806
6656
|
|
|
6807
|
-
// Core
|
|
6657
|
+
// Core handler (named so $duplicate etc. can recurse)
|
|
6808
6658
|
const handleMethod = async function (method, ...args) {
|
|
6809
|
-
// Clear error state before operation
|
|
6810
6659
|
clearErrorState();
|
|
6811
6660
|
|
|
6812
6661
|
try {
|
|
6813
|
-
// Get manifest to check if this is an Appwrite data source
|
|
6814
6662
|
const manifest = await window.ManifestDataConfig?.ensureManifest?.();
|
|
6815
6663
|
if (!manifest?.data) {
|
|
6816
6664
|
throw new Error('[Manifest Data] Manifest not available');
|
|
@@ -6861,7 +6709,7 @@ function createAppwriteMethodsHandler(dataSourceName, reloadDataSource) {
|
|
|
6861
6709
|
}
|
|
6862
6710
|
}
|
|
6863
6711
|
|
|
6864
|
-
//
|
|
6712
|
+
// Optimistic mutation path
|
|
6865
6713
|
const executeMutation = window.ManifestDataMutations?.executeMutation;
|
|
6866
6714
|
if (executeMutation) {
|
|
6867
6715
|
return await executeMutation({
|
|
@@ -7452,11 +7300,8 @@ function createAppwriteMethodsHandler(dataSourceName, reloadDataSource) {
|
|
|
7452
7300
|
addEntryToStore(dataSourceName, result);
|
|
7453
7301
|
}
|
|
7454
7302
|
|
|
7455
|
-
//
|
|
7456
|
-
//
|
|
7457
|
-
// 1. $x.assets.$create(file, null, null, { entryId: '...', table: 'projects' })
|
|
7458
|
-
// 2. $x.assets.$create(file, null, null, { entryId: '...', table: 'projects', fileIdsColumn: 'attachments' })
|
|
7459
|
-
// 3. $x.assets.$create(file, null, null, null, 'entryId') // Legacy: 4th arg as entryId
|
|
7303
|
+
// Link the uploaded file to a table entry, resolved from the
|
|
7304
|
+
// options object (4th arg), the legacy 5th-arg id, or belongsTo.
|
|
7460
7305
|
let entryId = null;
|
|
7461
7306
|
let tableName = null;
|
|
7462
7307
|
let fileIdsColumn = 'fileIds'; // Default column name
|
|
@@ -7507,11 +7352,8 @@ function createAppwriteMethodsHandler(dataSourceName, reloadDataSource) {
|
|
|
7507
7352
|
}
|
|
7508
7353
|
}
|
|
7509
7354
|
|
|
7510
|
-
//
|
|
7511
|
-
//
|
|
7512
|
-
// Background reload would only be needed if we need to apply scope filtering,
|
|
7513
|
-
// but since we have the real file object, we can trust it's correct
|
|
7514
|
-
// If scope filtering is needed, it will be handled by realtime events
|
|
7355
|
+
// We already have the real file from the API response, so no
|
|
7356
|
+
// background reload; scope filtering (if any) is handled by realtime.
|
|
7515
7357
|
if (!addEntryToStore) {
|
|
7516
7358
|
// Fallback to old behavior
|
|
7517
7359
|
if (window.ManifestDataStore?.dataSourceCache) {
|
|
@@ -7584,9 +7426,8 @@ function createAppwriteMethodsHandler(dataSourceName, reloadDataSource) {
|
|
|
7584
7426
|
})
|
|
7585
7427
|
);
|
|
7586
7428
|
|
|
7587
|
-
//
|
|
7588
|
-
// and
|
|
7589
|
-
// where stale data overwrites optimistic deletes, causing files to reappear.
|
|
7429
|
+
// No reload: optimistic delete + realtime sync. Reloading
|
|
7430
|
+
// races and can resurrect deleted files from stale data.
|
|
7590
7431
|
return results;
|
|
7591
7432
|
} else {
|
|
7592
7433
|
// Fallback to old behavior
|
|
@@ -7614,9 +7455,8 @@ function createAppwriteMethodsHandler(dataSourceName, reloadDataSource) {
|
|
|
7614
7455
|
}
|
|
7615
7456
|
});
|
|
7616
7457
|
|
|
7617
|
-
//
|
|
7618
|
-
// and
|
|
7619
|
-
// where stale data overwrites optimistic deletes, causing files to reappear.
|
|
7458
|
+
// No reload: optimistic delete + realtime sync. Reloading
|
|
7459
|
+
// races and can resurrect deleted files from stale data.
|
|
7620
7460
|
return result;
|
|
7621
7461
|
} else {
|
|
7622
7462
|
// Fallback to old behavior
|
|
@@ -7651,40 +7491,15 @@ function createAppwriteMethodsHandler(dataSourceName, reloadDataSource) {
|
|
|
7651
7491
|
throw new Error(`[Manifest Data] File "${actualFileId}" not found or not accessible: ${error.message}`);
|
|
7652
7492
|
}
|
|
7653
7493
|
|
|
7654
|
-
//
|
|
7655
|
-
// file content). Using 'view' rather than 'download' since
|
|
7656
|
-
// we're re-uploading rather than saving to disk.
|
|
7494
|
+
// 'view' URL (a string, not the bytes) since we re-upload rather than save to disk
|
|
7657
7495
|
const viewUrl = await window.ManifestDataAppwrite.getFileURL(bucketId, actualFileId);
|
|
7658
7496
|
|
|
7659
|
-
//
|
|
7660
|
-
//
|
|
7661
|
-
//
|
|
7662
|
-
// the
|
|
7663
|
-
//
|
|
7664
|
-
//
|
|
7665
|
-
// session cookie (SameSite=Lax on plain HTTP), so the
|
|
7666
|
-
// request lands at Appwrite as anonymous and a permissioned
|
|
7667
|
-
// file returns 404 storage_file_not_found.
|
|
7668
|
-
//
|
|
7669
|
-
// The Appwrite Web SDK works around this by writing the
|
|
7670
|
-
// session token to localStorage under `cookieFallback`
|
|
7671
|
-
// and replaying it as the `X-Fallback-Cookies` header on
|
|
7672
|
-
// every SDK request. That's why SDK calls (getFile metadata
|
|
7673
|
-
// above, listRows, $create, etc.) succeed cross-domain
|
|
7674
|
-
// while raw fetch() doesn't — raw fetch doesn't know to
|
|
7675
|
-
// read that localStorage key.
|
|
7676
|
-
//
|
|
7677
|
-
// We do exactly what the SDK does: read cookieFallback
|
|
7678
|
-
// and attach it as X-Fallback-Cookies. This is more
|
|
7679
|
-
// reliable than JWT:
|
|
7680
|
-
// - no createJWT round-trip (and dev-key-configured
|
|
7681
|
-
// clients 501 on createJWT)
|
|
7682
|
-
// - no 15-min expiry or rate limit (10/hour/account)
|
|
7683
|
-
// - matches whatever auth the SDK is already using
|
|
7684
|
-
//
|
|
7685
|
-
// Falls through to credentials-only when the user isn't
|
|
7686
|
-
// signed in (no cookieFallback in storage) — that path
|
|
7687
|
-
// still works in production with a SameSite=None cookie.
|
|
7497
|
+
// Storage /view checks the user SESSION, not API/dev keys. In
|
|
7498
|
+
// localhost dev the cross-domain session cookie is blocked
|
|
7499
|
+
// (SameSite=Lax on HTTP) so a raw fetch lands anonymous → 404.
|
|
7500
|
+
// Replicate the SDK's workaround: replay the `cookieFallback`
|
|
7501
|
+
// localStorage token as X-Fallback-Cookies. Falls through to
|
|
7502
|
+
// credentials-only when signed out (works in prod via SameSite=None).
|
|
7688
7503
|
const fetchHeaders = {};
|
|
7689
7504
|
if (appwriteConfig.projectId) {
|
|
7690
7505
|
fetchHeaders['X-Appwrite-Project'] = appwriteConfig.projectId;
|
|
@@ -8068,14 +7883,8 @@ window.ManifestDataProxiesAppwrite.createAppwriteMethodsHandler = createAppwrite
|
|
|
8068
7883
|
|
|
8069
7884
|
|
|
8070
7885
|
/* Manifest Data Sources - Magic Method State Properties */
|
|
8071
|
-
//
|
|
7886
|
+
// $loading, $error, $ready state properties
|
|
8072
7887
|
|
|
8073
|
-
/**
|
|
8074
|
-
* Get state property value for a data source
|
|
8075
|
-
* @param {string} prop - Property name ($loading, $error, $ready)
|
|
8076
|
-
* @param {string} dataSourceName - Name of the data source
|
|
8077
|
-
* @returns {boolean|string|null} State value
|
|
8078
|
-
*/
|
|
8079
7888
|
function getStateProperty(prop, dataSourceName) {
|
|
8080
7889
|
if (typeof Alpine === 'undefined' || !Alpine.store) {
|
|
8081
7890
|
return prop === '$loading' ? false : (prop === '$error' ? null : false);
|
|
@@ -8100,10 +7909,7 @@ function getStateProperty(prop, dataSourceName) {
|
|
|
8100
7909
|
return undefined;
|
|
8101
7910
|
}
|
|
8102
7911
|
|
|
8103
|
-
|
|
8104
|
-
* Create a state property handler for loading proxies
|
|
8105
|
-
* Returns a function that can be used in proxy get handlers
|
|
8106
|
-
*/
|
|
7912
|
+
// State property handler for use in proxy get() traps
|
|
8107
7913
|
function createStatePropertyHandler(dataSourceName) {
|
|
8108
7914
|
return function (key) {
|
|
8109
7915
|
if (key === '$loading' || key === '$error' || key === '$ready') {
|
|
@@ -8228,14 +8034,8 @@ window.ManifestDataProxiesMagic.createFilesMethod = createFilesMethod;
|
|
|
8228
8034
|
|
|
8229
8035
|
|
|
8230
8036
|
/* Manifest Data Sources - Magic Method $upload Handler */
|
|
8231
|
-
//
|
|
8037
|
+
// $upload: upload files and link them to a table entry
|
|
8232
8038
|
|
|
8233
|
-
/**
|
|
8234
|
-
* Create $upload method for a data source
|
|
8235
|
-
* @param {string} dataSourceName - Name of the data source (table)
|
|
8236
|
-
* @param {Function} reloadDataSource - Function to reload data source
|
|
8237
|
-
* @returns {Function} $upload method function
|
|
8238
|
-
*/
|
|
8239
8039
|
function createUploadMethod(dataSourceName, reloadDataSource) {
|
|
8240
8040
|
return async function (entryId, fileOrEvent, bucketName) {
|
|
8241
8041
|
const manifest = await window.ManifestDataConfig.ensureManifest();
|
|
@@ -8350,10 +8150,7 @@ function createUploadMethod(dataSourceName, reloadDataSource) {
|
|
|
8350
8150
|
|
|
8351
8151
|
const results = await Promise.all(uploadPromises);
|
|
8352
8152
|
|
|
8353
|
-
//
|
|
8354
|
-
// automatically update when bucket array changes (single source of truth)
|
|
8355
|
-
|
|
8356
|
-
// Return single file or array of files
|
|
8153
|
+
// Computed files arrays update automatically when the bucket array changes.
|
|
8357
8154
|
return files.length === 1 ? results[0] : results;
|
|
8358
8155
|
} catch (error) {
|
|
8359
8156
|
// Clear all uploading states on error
|
|
@@ -8376,14 +8173,8 @@ window.ManifestDataProxiesMagic.createUploadMethod = createUploadMethod;
|
|
|
8376
8173
|
|
|
8377
8174
|
|
|
8378
8175
|
/* Manifest Data Sources - Magic Method Pagination Handlers */
|
|
8379
|
-
//
|
|
8176
|
+
// $first, $next, $prev, $page
|
|
8380
8177
|
|
|
8381
|
-
/**
|
|
8382
|
-
* Create pagination method handler
|
|
8383
|
-
* @param {string} methodName - Method name ($first, $next, $prev, $page)
|
|
8384
|
-
* @param {string} dataSourceName - Name of the data source
|
|
8385
|
-
* @returns {Function} Pagination method function
|
|
8386
|
-
*/
|
|
8387
8178
|
function createPaginationMethod(methodName, dataSourceName) {
|
|
8388
8179
|
return async function (...args) {
|
|
8389
8180
|
const manifest = await window.ManifestDataConfig.ensureManifest();
|
|
@@ -8396,7 +8187,7 @@ function createPaginationMethod(methodName, dataSourceName) {
|
|
|
8396
8187
|
throw new Error(`[Manifest Data] Pagination is only supported for Appwrite data sources`);
|
|
8397
8188
|
}
|
|
8398
8189
|
|
|
8399
|
-
//
|
|
8190
|
+
// Base queries from manifest or scope
|
|
8400
8191
|
const scope = window.ManifestDataConfig.getScope(dataSource);
|
|
8401
8192
|
const queriesConfig = window.ManifestDataConfig.getQueries(dataSource);
|
|
8402
8193
|
const baseQueries = queriesConfig
|
|
@@ -8437,10 +8228,9 @@ window.ManifestDataProxiesMagic.createPaginationMethod = createPaginationMethod;
|
|
|
8437
8228
|
|
|
8438
8229
|
|
|
8439
8230
|
/* Manifest Data Sources - Magic Method Core Registration */
|
|
8440
|
-
//
|
|
8231
|
+
// $x proxy creation/registration; delegates to helper modules.
|
|
8441
8232
|
|
|
8442
|
-
// Expose
|
|
8443
|
-
// This ensures window.$x works in x-data methods and other contexts
|
|
8233
|
+
// Expose window.$x at module load (before Alpine) so it works in x-data methods
|
|
8444
8234
|
if (typeof window !== 'undefined') {
|
|
8445
8235
|
// Create a cached fallback proxy (reuse same instance for chaining)
|
|
8446
8236
|
let cachedFallbackProxy = null;
|
|
@@ -8449,9 +8239,7 @@ if (typeof window !== 'undefined') {
|
|
|
8449
8239
|
try {
|
|
8450
8240
|
Object.defineProperty(window, '$x', {
|
|
8451
8241
|
get: function () {
|
|
8452
|
-
//
|
|
8453
|
-
|
|
8454
|
-
// 1. Try stored factory function (most reliable)
|
|
8242
|
+
// 1. Stored factory (most reliable)
|
|
8455
8243
|
if (window._$xProxyFactory && typeof window._$xProxyFactory === 'function') {
|
|
8456
8244
|
try {
|
|
8457
8245
|
const proxy = window._$xProxyFactory();
|
|
@@ -8461,7 +8249,7 @@ if (typeof window !== 'undefined') {
|
|
|
8461
8249
|
}
|
|
8462
8250
|
}
|
|
8463
8251
|
|
|
8464
|
-
// 2.
|
|
8252
|
+
// 2. Alpine magic method
|
|
8465
8253
|
try {
|
|
8466
8254
|
const magicFn = window.Alpine?.magic?.('x');
|
|
8467
8255
|
if (magicFn && typeof magicFn === 'function') {
|
|
@@ -8469,28 +8257,23 @@ if (typeof window !== 'undefined') {
|
|
|
8469
8257
|
if (proxy) return proxy;
|
|
8470
8258
|
}
|
|
8471
8259
|
} catch (e) {
|
|
8472
|
-
//
|
|
8260
|
+
// Not ready — continue to fallback
|
|
8473
8261
|
}
|
|
8474
8262
|
|
|
8475
|
-
// 3.
|
|
8476
|
-
// This allows code to run without errors while Alpine initializes
|
|
8477
|
-
// Use the same loading proxy pattern used elsewhere in the codebase
|
|
8263
|
+
// 3. Loading proxy (lets code run while Alpine initializes)
|
|
8478
8264
|
const createLoadingProxy = window.ManifestDataProxiesCore?.createLoadingProxy;
|
|
8479
8265
|
if (createLoadingProxy) {
|
|
8480
8266
|
const loadingProxy = createLoadingProxy();
|
|
8481
8267
|
if (loadingProxy) return loadingProxy;
|
|
8482
8268
|
}
|
|
8483
8269
|
|
|
8484
|
-
//
|
|
8485
|
-
// Cache it so chaining works (window.$x.projects.$upload returns the same proxy)
|
|
8270
|
+
// 4. Cached self-chaining proxy (same instance keeps chaining safe)
|
|
8486
8271
|
if (!cachedFallbackProxy) {
|
|
8487
8272
|
cachedFallbackProxy = new Proxy({}, {
|
|
8488
8273
|
get(target, prop) {
|
|
8489
|
-
// Return the same proxy for chaining (allows window.$x.projects.$upload without errors)
|
|
8490
8274
|
return cachedFallbackProxy;
|
|
8491
8275
|
},
|
|
8492
8276
|
has(target, prop) {
|
|
8493
|
-
// Make all properties appear to exist to prevent Alpine errors
|
|
8494
8277
|
return true;
|
|
8495
8278
|
}
|
|
8496
8279
|
});
|
|
@@ -8506,12 +8289,7 @@ if (typeof window !== 'undefined') {
|
|
|
8506
8289
|
}
|
|
8507
8290
|
}
|
|
8508
8291
|
|
|
8509
|
-
|
|
8510
|
-
* Create a loading proxy with methods for Appwrite data sources
|
|
8511
|
-
* @param {string} dataSourceName - Name of the data source
|
|
8512
|
-
* @param {Function} reloadDataSource - Function to reload data source
|
|
8513
|
-
* @returns {Proxy} Loading proxy with methods
|
|
8514
|
-
*/
|
|
8292
|
+
// Loading proxy for Appwrite sources (CRUD + state/files/upload/pagination methods)
|
|
8515
8293
|
function createAppwriteLoadingProxy(dataSourceName, reloadDataSource) {
|
|
8516
8294
|
const createLoadingProxy = window.ManifestDataProxiesCore?.createLoadingProxy;
|
|
8517
8295
|
const createAppwriteMethodsHandler = window.ManifestDataProxiesAppwrite?.createAppwriteMethodsHandler;
|
|
@@ -8534,7 +8312,6 @@ function createAppwriteLoadingProxy(dataSourceName, reloadDataSource) {
|
|
|
8534
8312
|
|
|
8535
8313
|
return new Proxy(createLoadingProxy(), {
|
|
8536
8314
|
get(target, key) {
|
|
8537
|
-
// Handle state properties
|
|
8538
8315
|
if (stateHandler) {
|
|
8539
8316
|
const stateValue = stateHandler(key);
|
|
8540
8317
|
if (stateValue !== undefined) {
|
|
@@ -8542,22 +8319,19 @@ function createAppwriteLoadingProxy(dataSourceName, reloadDataSource) {
|
|
|
8542
8319
|
}
|
|
8543
8320
|
}
|
|
8544
8321
|
|
|
8545
|
-
//
|
|
8322
|
+
// $files / $upload available even while loading
|
|
8546
8323
|
if (key === '$files' && filesMethod) {
|
|
8547
8324
|
return filesMethod;
|
|
8548
8325
|
}
|
|
8549
|
-
|
|
8550
|
-
// Handle $upload method for tables - available even when loading
|
|
8551
8326
|
if (key === '$upload' && uploadMethod) {
|
|
8552
8327
|
return uploadMethod;
|
|
8553
8328
|
}
|
|
8554
8329
|
|
|
8555
|
-
// Handle pagination methods
|
|
8556
8330
|
if ((key === '$first' || key === '$next' || key === '$prev' || key === '$page') && createPaginationMethod) {
|
|
8557
8331
|
return createPaginationMethod(key, dataSourceName);
|
|
8558
8332
|
}
|
|
8559
8333
|
|
|
8560
|
-
//
|
|
8334
|
+
// Appwrite CRUD
|
|
8561
8335
|
if (key === '$create' || key === '$update' || key === '$delete' || key === '$query' ||
|
|
8562
8336
|
key === '$url' || key === '$download' || key === '$preview' || key === '$filesFor' ||
|
|
8563
8337
|
key === '$unlinkFrom' || key === '$removeFrom' || key === '$remove') {
|
|
@@ -8566,17 +8340,12 @@ function createAppwriteLoadingProxy(dataSourceName, reloadDataSource) {
|
|
|
8566
8340
|
}
|
|
8567
8341
|
}
|
|
8568
8342
|
|
|
8569
|
-
// Fall through to loading proxy
|
|
8570
8343
|
return target[key];
|
|
8571
8344
|
}
|
|
8572
8345
|
});
|
|
8573
8346
|
}
|
|
8574
8347
|
|
|
8575
|
-
|
|
8576
|
-
* Create a loading proxy for non-Appwrite data sources
|
|
8577
|
-
* @param {string} dataSourceName - Name of the data source
|
|
8578
|
-
* @returns {Proxy} Loading proxy with basic methods
|
|
8579
|
-
*/
|
|
8348
|
+
// Loading proxy for non-Appwrite sources (state + $files only)
|
|
8580
8349
|
function createBasicLoadingProxy(dataSourceName) {
|
|
8581
8350
|
const createLoadingProxy = window.ManifestDataProxiesCore?.createLoadingProxy;
|
|
8582
8351
|
const createStatePropertyHandler = window.ManifestDataProxiesMagic?.createStatePropertyHandler;
|
|
@@ -8591,7 +8360,6 @@ function createBasicLoadingProxy(dataSourceName) {
|
|
|
8591
8360
|
|
|
8592
8361
|
return new Proxy(createLoadingProxy(), {
|
|
8593
8362
|
get(target, key) {
|
|
8594
|
-
// Handle state properties
|
|
8595
8363
|
if (stateHandler) {
|
|
8596
8364
|
const stateValue = stateHandler(key);
|
|
8597
8365
|
if (stateValue !== undefined) {
|
|
@@ -8599,7 +8367,6 @@ function createBasicLoadingProxy(dataSourceName) {
|
|
|
8599
8367
|
}
|
|
8600
8368
|
}
|
|
8601
8369
|
|
|
8602
|
-
// Handle $files method for tables (reactive file arrays)
|
|
8603
8370
|
if (key === '$files' && filesMethod) {
|
|
8604
8371
|
return filesMethod;
|
|
8605
8372
|
}
|
|
@@ -8609,23 +8376,16 @@ function createBasicLoadingProxy(dataSourceName) {
|
|
|
8609
8376
|
});
|
|
8610
8377
|
}
|
|
8611
8378
|
|
|
8612
|
-
|
|
8613
|
-
* Register the $x magic method with Alpine
|
|
8614
|
-
* @param {Function} loadDataSource - Function to load data sources
|
|
8615
|
-
*/
|
|
8379
|
+
// Register the $x magic method with Alpine
|
|
8616
8380
|
function registerXMagicMethod(loadDataSource) {
|
|
8617
|
-
// Ensure Alpine is loaded before registering magic method
|
|
8618
8381
|
if (typeof Alpine === 'undefined') {
|
|
8619
8382
|
console.error('[Manifest Data] Alpine.js must be loaded before manifest.data.js');
|
|
8620
8383
|
return;
|
|
8621
8384
|
}
|
|
8622
8385
|
|
|
8623
|
-
// Store the proxy-creating function so we can access it directly
|
|
8624
8386
|
let $xProxyFactory = null;
|
|
8625
8387
|
|
|
8626
|
-
//
|
|
8627
|
-
// works. If we created a new proxy per magic('x') call, each would have its own depth and we'd
|
|
8628
|
-
// never see depth > 1 when Alpine re-enters during store reads.
|
|
8388
|
+
// Same proxy instance every call so the magicGetDepth re-entrancy guard actually counts re-entries.
|
|
8629
8389
|
let cachedMagicProxy = null;
|
|
8630
8390
|
|
|
8631
8391
|
const magicFunction = (el) => {
|
|
@@ -8641,13 +8401,10 @@ function registerXMagicMethod(loadDataSource) {
|
|
|
8641
8401
|
// Store loadDataSource in closure for Appwrite methods
|
|
8642
8402
|
const reloadDataSource = loadDataSource;
|
|
8643
8403
|
|
|
8644
|
-
// Track active property accesses to prevent circular references
|
|
8645
|
-
// Use a symbol to store the active props set on each proxy call
|
|
8646
8404
|
const ACTIVE_PROPS = Symbol('activeProps');
|
|
8647
8405
|
|
|
8648
|
-
// Re-entrancy guard
|
|
8649
|
-
//
|
|
8650
|
-
// we read Alpine.store('data'), and we must return real data for that to render.
|
|
8406
|
+
// Re-entrancy guard against store-read recursion → stack overflow. High
|
|
8407
|
+
// threshold so we only break true overflow (Alpine may re-enter once).
|
|
8651
8408
|
let magicGetDepth = 0;
|
|
8652
8409
|
const MAGIC_GET_MAX_DEPTH = 12;
|
|
8653
8410
|
|
|
@@ -8662,17 +8419,14 @@ function registerXMagicMethod(loadDataSource) {
|
|
|
8662
8419
|
return fallback !== undefined ? fallback : '';
|
|
8663
8420
|
}
|
|
8664
8421
|
try {
|
|
8665
|
-
// Handle special keys
|
|
8666
8422
|
if (prop === Symbol.iterator || prop === 'then' || prop === 'catch' || prop === 'finally') {
|
|
8667
8423
|
return undefined;
|
|
8668
8424
|
}
|
|
8669
8425
|
|
|
8670
|
-
//
|
|
8671
|
-
//
|
|
8672
|
-
//
|
|
8673
|
-
//
|
|
8674
|
-
// By using getRawData (non-reactive) first and caching the nested proxy, the re-run hits
|
|
8675
|
-
// the cache and returns without touching the store.
|
|
8426
|
+
// Resolve+cache from raw data BEFORE reading Alpine.store('data'):
|
|
8427
|
+
// the store read registers a reactive dep that can re-run this
|
|
8428
|
+
// effect and re-enter get() → stack overflow. Caching first makes
|
|
8429
|
+
// the re-run hit the cache without touching the store.
|
|
8676
8430
|
const getRawDataEarly = window.ManifestDataStore?.getRawData;
|
|
8677
8431
|
const rawValueEarly = getRawDataEarly ? getRawDataEarly(prop) : null;
|
|
8678
8432
|
if (!window.ManifestDataProxiesCore.nestedDataSourceProxyCache) {
|
|
@@ -8691,7 +8445,7 @@ function registerXMagicMethod(loadDataSource) {
|
|
|
8691
8445
|
}
|
|
8692
8446
|
}
|
|
8693
8447
|
if (nestedCache.has(prop) && !hasData) nestedCache.delete(prop);
|
|
8694
|
-
//
|
|
8448
|
+
// Object sources (content, manifest): build+cache from raw before any store read
|
|
8695
8449
|
if (hasData && rawValueEarly && typeof rawValueEarly === 'object' && !Array.isArray(rawValueEarly)) {
|
|
8696
8450
|
const createNestedObjectProxy = window.ManifestDataProxies?.createNestedObjectProxy;
|
|
8697
8451
|
if (createNestedObjectProxy) {
|
|
@@ -8709,10 +8463,9 @@ function registerXMagicMethod(loadDataSource) {
|
|
|
8709
8463
|
}
|
|
8710
8464
|
}
|
|
8711
8465
|
|
|
8712
|
-
//
|
|
8713
|
-
//
|
|
8714
|
-
//
|
|
8715
|
-
// never return store data, so no re-entry/stack overflow.
|
|
8466
|
+
// No raw data yet: start load, subscribe (read _dataVersion so
|
|
8467
|
+
// Alpine tracks the dep and re-runs on updateStore), return loading
|
|
8468
|
+
// proxy. Reading only the version avoids re-entry/overflow.
|
|
8716
8469
|
if (!hasData) {
|
|
8717
8470
|
if (!pendingLoads.has(prop)) {
|
|
8718
8471
|
const locale = typeof document !== 'undefined' && document.documentElement
|
|
@@ -8736,10 +8489,9 @@ function registerXMagicMethod(loadDataSource) {
|
|
|
8736
8489
|
const currentStoreForCache = Alpine.store('data');
|
|
8737
8490
|
void (currentStoreForCache && currentStoreForCache._dataVersion);
|
|
8738
8491
|
|
|
8739
|
-
//
|
|
8740
|
-
//
|
|
8741
|
-
//
|
|
8742
|
-
// broke rendering. Stack overflow is prevented by MAGIC_GET_MAX_DEPTH instead.
|
|
8492
|
+
// No activeProps circular check here: a legit store-read re-entry
|
|
8493
|
+
// would be misread as circular and return a loading proxy, breaking
|
|
8494
|
+
// rendering. MAGIC_GET_MAX_DEPTH guards overflow instead.
|
|
8743
8495
|
const propKey = String(prop);
|
|
8744
8496
|
const activeProps = target[ACTIVE_PROPS] || (target[ACTIVE_PROPS] = new Set());
|
|
8745
8497
|
|
|
@@ -8752,16 +8504,12 @@ function registerXMagicMethod(loadDataSource) {
|
|
|
8752
8504
|
return undefined;
|
|
8753
8505
|
}
|
|
8754
8506
|
|
|
8755
|
-
// Get raw data first (unproxied) to check if it's an array
|
|
8756
8507
|
const getRawData = window.ManifestDataStore?.getRawData;
|
|
8757
8508
|
const rawValue = getRawData ? getRawData(prop) : null;
|
|
8758
8509
|
|
|
8759
|
-
//
|
|
8760
|
-
// CRITICAL: We need to access currentStore[prop] for Alpine reactivity to work
|
|
8761
|
-
// But we'll use rawValue when creating nested proxies to avoid circular references
|
|
8510
|
+
// Read currentStore[prop] for reactivity, but build nested proxies from rawValue
|
|
8762
8511
|
let value = currentStore[prop];
|
|
8763
8512
|
|
|
8764
|
-
// If value exists in store, return it immediately with proper proxy
|
|
8765
8513
|
if (value !== undefined && value !== null || rawValue !== undefined && rawValue !== null) {
|
|
8766
8514
|
// Clear any cached loading proxy for this data source
|
|
8767
8515
|
const globalAccessCache = window.ManifestDataProxies?.globalAccessCache;
|
|
@@ -8834,33 +8582,23 @@ function registerXMagicMethod(loadDataSource) {
|
|
|
8834
8582
|
}
|
|
8835
8583
|
|
|
8836
8584
|
if (arrayToProxy && (Array.isArray(arrayToProxy) || rawIsArrayLike || valueIsArrayLike)) {
|
|
8837
|
-
//
|
|
8838
|
-
//
|
|
8839
|
-
// By attaching methods directly to the array, Alpine can see them even when it wraps
|
|
8585
|
+
// attachArrayMethods (not createArrayProxyWithRoute): methods live on
|
|
8586
|
+
// the array itself so Alpine sees them even after wrapping our proxy.
|
|
8840
8587
|
const attachArrayMethods = window.ManifestDataProxies?.attachArrayMethods;
|
|
8841
8588
|
const arrayForMethods = valueIsArray ? value : (rawIsArray ? rawValue : arrayToProxy);
|
|
8842
8589
|
|
|
8843
8590
|
if (attachArrayMethods) {
|
|
8844
8591
|
const arrayWithMethods = attachArrayMethods(arrayForMethods, prop, loadDataSource);
|
|
8845
|
-
//
|
|
8846
|
-
// Alpine uses has() to check if properties exist before accessing them
|
|
8592
|
+
// has() trap so Alpine sees $search/$query/etc. before accessing
|
|
8847
8593
|
return new Proxy(arrayWithMethods, {
|
|
8848
8594
|
get(target, key) {
|
|
8849
|
-
// CRITICAL: Explicitly handle base plugin methods first
|
|
8850
8595
|
if (key === '$search' || key === '$query' || key === '$route') {
|
|
8851
8596
|
if (key in target && typeof target[key] === 'function') {
|
|
8852
8597
|
return target[key].bind(target);
|
|
8853
8598
|
}
|
|
8854
|
-
// Appwrite sources
|
|
8855
|
-
//
|
|
8856
|
-
//
|
|
8857
|
-
// Appwrite plugin"). Delegate to the Appwrite methods
|
|
8858
|
-
// handler so the click hits the backend instead of
|
|
8859
|
-
// silently falling through to `undefined` or, in some
|
|
8860
|
-
// proxy paths, a no-op `() => []`. Without this, demo
|
|
8861
|
-
// sort/query buttons appear to do nothing — no console
|
|
8862
|
-
// error, no network request — because the call resolves
|
|
8863
|
-
// to the chaining fallback's stub `queryFn`.
|
|
8599
|
+
// Appwrite sources skip client-side $query attachment;
|
|
8600
|
+
// delegate to the Appwrite handler so the call hits the
|
|
8601
|
+
// backend instead of the no-op chaining fallback.
|
|
8864
8602
|
if (key === '$query' || key === '$search') {
|
|
8865
8603
|
const createAppwriteMethodsHandler = window.ManifestDataProxiesAppwrite?.createAppwriteMethodsHandler;
|
|
8866
8604
|
if (createAppwriteMethodsHandler) {
|
|
@@ -8920,33 +8658,24 @@ function registerXMagicMethod(loadDataSource) {
|
|
|
8920
8658
|
}
|
|
8921
8659
|
}
|
|
8922
8660
|
|
|
8923
|
-
//
|
|
8661
|
+
// Objects: cache the nested proxy per source so Alpine
|
|
8662
|
+
// re-evaluations get the same instance (a fresh proxy each
|
|
8663
|
+
// time looks "new" and re-triggers evaluation → infinite loop).
|
|
8924
8664
|
if (value && typeof value === 'object' && !Array.isArray(value) && value !== null) {
|
|
8925
|
-
// Use nested object proxy to handle arrays within objects
|
|
8926
|
-
// CRITICAL: Cache nested proxies at $x level to prevent infinite loops
|
|
8927
|
-
// When Alpine re-evaluates expressions, it accesses $x.example again
|
|
8928
|
-
// If we create a new proxy each time, Alpine sees it as a "new" object and re-evaluates again
|
|
8929
8665
|
if (!window.ManifestDataProxiesCore.nestedDataSourceProxyCache) {
|
|
8930
8666
|
window.ManifestDataProxiesCore.nestedDataSourceProxyCache = new Map();
|
|
8931
8667
|
}
|
|
8932
8668
|
const nestedCache = window.ManifestDataProxiesCore.nestedDataSourceProxyCache;
|
|
8933
8669
|
|
|
8934
|
-
//
|
|
8935
|
-
// CRITICAL: Always return cached proxy if it exists to prevent infinite loops
|
|
8936
|
-
// When Alpine re-evaluates expressions, it accesses $x.example again
|
|
8937
|
-
// If we return the same proxy instance, Alpine won't see it as "new" and won't re-evaluate
|
|
8938
|
-
// NOTE: This cache check is redundant now (we check earlier), but keeping for safety
|
|
8670
|
+
// Redundant with the earlier check, kept for safety
|
|
8939
8671
|
if (nestedCache.has(prop)) {
|
|
8940
8672
|
const cachedProxy = nestedCache.get(prop);
|
|
8941
8673
|
if (cachedProxy) {
|
|
8942
8674
|
activeProps.delete(propKey);
|
|
8943
8675
|
return cachedProxy;
|
|
8944
|
-
} else {
|
|
8945
8676
|
}
|
|
8946
|
-
} else {
|
|
8947
8677
|
}
|
|
8948
8678
|
|
|
8949
|
-
// Clear cache entry if it exists but is invalid (safety check)
|
|
8950
8679
|
if (nestedCache.has(prop) && !nestedCache.get(prop)) {
|
|
8951
8680
|
nestedCache.delete(prop);
|
|
8952
8681
|
}
|
|
@@ -8954,10 +8683,8 @@ function registerXMagicMethod(loadDataSource) {
|
|
|
8954
8683
|
const createNestedObjectProxy = window.ManifestDataProxies?.createNestedObjectProxy;
|
|
8955
8684
|
if (createNestedObjectProxy) {
|
|
8956
8685
|
|
|
8957
|
-
//
|
|
8958
|
-
//
|
|
8959
|
-
// and accesses properties (e.g. :aria-label="$x.content.theme.light"), triggering
|
|
8960
|
-
// reactivity that re-evaluates the expression and re-enters this get → stack overflow.
|
|
8686
|
+
// MUST build from rawValue, never the Alpine-wrapped value —
|
|
8687
|
+
// wrapping our proxy and reading it re-triggers reactivity → overflow.
|
|
8961
8688
|
if (!rawValue || typeof rawValue !== 'object' || Array.isArray(rawValue)) {
|
|
8962
8689
|
activeProps.delete(propKey);
|
|
8963
8690
|
const createLoadingProxy = window.ManifestDataProxiesCore?.createLoadingProxy;
|
|
@@ -8976,21 +8703,15 @@ function registerXMagicMethod(loadDataSource) {
|
|
|
8976
8703
|
return rawValue;
|
|
8977
8704
|
}
|
|
8978
8705
|
|
|
8979
|
-
// Cache the nested proxy - this prevents creating new proxies on each access
|
|
8980
8706
|
if (nestedProxy) {
|
|
8981
8707
|
nestedCache.set(prop, nestedProxy);
|
|
8982
8708
|
} else {
|
|
8983
|
-
// If nested proxy creation failed, return raw value as fallback
|
|
8984
8709
|
activeProps.delete(propKey);
|
|
8985
8710
|
return rawValue;
|
|
8986
8711
|
}
|
|
8987
8712
|
|
|
8988
|
-
//
|
|
8989
|
-
// This ensures the proxy is cached before Alpine can trigger another access
|
|
8713
|
+
// Clear activeProps after caching, before returning
|
|
8990
8714
|
activeProps.delete(propKey);
|
|
8991
|
-
|
|
8992
|
-
// CRITICAL: Return the cached proxy immediately
|
|
8993
|
-
// Don't do anything else that might trigger Alpine reactivity
|
|
8994
8715
|
return nestedProxy;
|
|
8995
8716
|
}
|
|
8996
8717
|
}
|
|
@@ -9084,19 +8805,16 @@ function registerXMagicMethod(loadDataSource) {
|
|
|
9084
8805
|
});
|
|
9085
8806
|
pendingLoads.set(prop, loadPromise);
|
|
9086
8807
|
|
|
9087
|
-
//
|
|
9088
|
-
//
|
|
9089
|
-
// CRITICAL: Use attachArrayMethods to attach methods directly to the array
|
|
9090
|
-
// This ensures Alpine can see them even when it wraps the array
|
|
8808
|
+
// Array sources: empty array with methods attached, so
|
|
8809
|
+
// .map()/.filter() work before data loads (and Alpine sees them).
|
|
9091
8810
|
if (isArrayDataSource) {
|
|
9092
8811
|
const emptyArray = [];
|
|
9093
8812
|
const attachArrayMethods = window.ManifestDataProxies?.attachArrayMethods;
|
|
9094
8813
|
if (attachArrayMethods) {
|
|
9095
8814
|
const arrayWithMethods = attachArrayMethods(emptyArray, prop, reloadDataSource);
|
|
9096
|
-
//
|
|
8815
|
+
// has() trap so Alpine sees $search/$query/etc.
|
|
9097
8816
|
return new Proxy(arrayWithMethods, {
|
|
9098
8817
|
get(target, key) {
|
|
9099
|
-
// CRITICAL: Explicitly handle base plugin methods first
|
|
9100
8818
|
if (key === '$search' || key === '$query' || key === '$route') {
|
|
9101
8819
|
if (key in target && typeof target[key] === 'function') {
|
|
9102
8820
|
return target[key].bind(target);
|
|
@@ -9183,19 +8901,15 @@ function registerXMagicMethod(loadDataSource) {
|
|
|
9183
8901
|
const attachArrayMethods = window.ManifestDataProxies?.attachArrayMethods;
|
|
9184
8902
|
if (attachArrayMethods) {
|
|
9185
8903
|
const arrayWithMethods = attachArrayMethods(value, prop, loadDataSource);
|
|
9186
|
-
//
|
|
8904
|
+
// has() trap so Alpine sees $search/$query/etc.
|
|
9187
8905
|
return new Proxy(arrayWithMethods, {
|
|
9188
8906
|
get(target, key) {
|
|
9189
|
-
// Handle base plugin methods with fallbacks
|
|
9190
8907
|
if (key === '$search' || key === '$query') {
|
|
9191
8908
|
if (target && typeof target === 'object' && key in target && typeof target[key] === 'function') {
|
|
9192
8909
|
return target[key].bind(target);
|
|
9193
8910
|
}
|
|
9194
|
-
// Appwrite
|
|
9195
|
-
//
|
|
9196
|
-
// (it requires a backend round-trip). Route to the
|
|
9197
|
-
// Appwrite methods handler instead of the no-op fallback
|
|
9198
|
-
// so sort/query/search buttons actually fire requests.
|
|
8911
|
+
// Appwrite $query isn't attached (needs a backend round-trip);
|
|
8912
|
+
// route to the Appwrite handler so buttons actually fire requests.
|
|
9199
8913
|
const createAppwriteMethodsHandler = window.ManifestDataProxiesAppwrite?.createAppwriteMethodsHandler;
|
|
9200
8914
|
if (createAppwriteMethodsHandler) {
|
|
9201
8915
|
try {
|
|
@@ -9268,15 +8982,13 @@ function registerXMagicMethod(loadDataSource) {
|
|
|
9268
8982
|
}
|
|
9269
8983
|
return value;
|
|
9270
8984
|
|
|
9271
|
-
//
|
|
8985
|
+
// NOTE: unreachable below (early return above); kept for reference.
|
|
9272
8986
|
const dataSourceProxy = new Proxy(value, {
|
|
9273
8987
|
get(target, key) {
|
|
9274
|
-
// Handle special keys
|
|
9275
8988
|
if (key === 'then' || key === 'catch' || key === 'finally') {
|
|
9276
8989
|
return undefined;
|
|
9277
8990
|
}
|
|
9278
8991
|
|
|
9279
|
-
// Handle state properties
|
|
9280
8992
|
const stateHandler = window.ManifestDataProxiesMagic?.createStatePropertyHandler;
|
|
9281
8993
|
if (stateHandler) {
|
|
9282
8994
|
const stateValue = stateHandler(prop)(key);
|
|
@@ -9632,9 +9344,8 @@ function registerXMagicMethod(loadDataSource) {
|
|
|
9632
9344
|
const createLoadingProxy = window.ManifestDataProxiesCore?.createLoadingProxy;
|
|
9633
9345
|
return createLoadingProxy ? createLoadingProxy(prop) : {};
|
|
9634
9346
|
} finally {
|
|
9635
|
-
// Always
|
|
9636
|
-
//
|
|
9637
|
-
// get(proxy, prop) was wrongly treated as CIRCULAR.
|
|
9347
|
+
// Always clear so the same prop can be read again (else Alpine's
|
|
9348
|
+
// next read of it is wrongly treated as circular).
|
|
9638
9349
|
activeProps.delete(propKey);
|
|
9639
9350
|
}
|
|
9640
9351
|
} finally {
|
|
@@ -9672,8 +9383,7 @@ function registerXMagicMethod(loadDataSource) {
|
|
|
9672
9383
|
}
|
|
9673
9384
|
}
|
|
9674
9385
|
|
|
9675
|
-
//
|
|
9676
|
-
// Usage: $try(() => $x.assets.$removeFrom(...), 'assetsError')
|
|
9386
|
+
// $try magic: async error handling — $try(() => $x.assets.$removeFrom(...), 'assetsError')
|
|
9677
9387
|
if (typeof Alpine !== 'undefined' && typeof Alpine.magic === 'function') {
|
|
9678
9388
|
if (!window.__manifestTryMagicRegistered) {
|
|
9679
9389
|
window.__manifestTryMagicRegistered = true;
|
|
@@ -9695,15 +9405,12 @@ if (typeof Alpine !== 'undefined' && typeof Alpine.magic === 'function') {
|
|
|
9695
9405
|
scope[errorVar] = error.message || 'Operation failed';
|
|
9696
9406
|
}
|
|
9697
9407
|
}
|
|
9698
|
-
//
|
|
9408
|
+
// Return undefined on error so the caller can handle it gracefully
|
|
9699
9409
|
return undefined;
|
|
9700
9410
|
}
|
|
9701
9411
|
};
|
|
9702
9412
|
});
|
|
9703
9413
|
}
|
|
9704
|
-
|
|
9705
|
-
// Note: window.$x getter is defined at module load time (top of file)
|
|
9706
|
-
// so it's available immediately, even before registerXMagicMethod is called
|
|
9707
9414
|
}
|
|
9708
9415
|
|
|
9709
9416
|
// Clear nested proxy cache for a specific data source (called when store updates)
|
|
@@ -9712,7 +9419,7 @@ function clearNestedProxyCacheForDataSource(dataSourceName) {
|
|
|
9712
9419
|
window.ManifestDataProxiesCore.nestedDataSourceProxyCache.delete(dataSourceName);
|
|
9713
9420
|
}
|
|
9714
9421
|
|
|
9715
|
-
//
|
|
9422
|
+
// Exports
|
|
9716
9423
|
if (!window.ManifestDataProxies) {
|
|
9717
9424
|
window.ManifestDataProxies = {};
|
|
9718
9425
|
}
|
|
@@ -9720,10 +9427,9 @@ window.ManifestDataProxies.registerXMagicMethod = registerXMagicMethod;
|
|
|
9720
9427
|
window.ManifestDataProxies.clearNestedProxyCacheForDataSource = clearNestedProxyCacheForDataSource;
|
|
9721
9428
|
|
|
9722
9429
|
/* Manifest Data Sources - Directives */
|
|
9723
|
-
//
|
|
9430
|
+
// x-files / x-data-files: reactive file arrays for a table entry
|
|
9724
9431
|
function registerFilesDirective() {
|
|
9725
9432
|
if (typeof Alpine === 'undefined') {
|
|
9726
|
-
// Wait for Alpine to be available
|
|
9727
9433
|
const checkAlpine = setInterval(() => {
|
|
9728
9434
|
if (typeof Alpine !== 'undefined') {
|
|
9729
9435
|
clearInterval(checkAlpine);
|
|
@@ -9734,12 +9440,10 @@ function registerFilesDirective() {
|
|
|
9734
9440
|
return;
|
|
9735
9441
|
}
|
|
9736
9442
|
|
|
9737
|
-
//
|
|
9443
|
+
// Walk up to the nearest parent x-for template
|
|
9738
9444
|
function findParentXFor(el) {
|
|
9739
9445
|
let current = el;
|
|
9740
|
-
// Walk up the tree, checking each element
|
|
9741
9446
|
while (current) {
|
|
9742
|
-
// Check if current element is a template with x-for
|
|
9743
9447
|
if (current.tagName === 'TEMPLATE' && current.hasAttribute('x-for')) {
|
|
9744
9448
|
return current;
|
|
9745
9449
|
}
|
|
@@ -9752,17 +9456,15 @@ function registerFilesDirective() {
|
|
|
9752
9456
|
return null;
|
|
9753
9457
|
}
|
|
9754
9458
|
|
|
9755
|
-
//
|
|
9459
|
+
// "item in $x.source" -> "item"
|
|
9756
9460
|
function extractLoopItemName(xForExpression) {
|
|
9757
|
-
// x-for="item in $x.source" -> "item"
|
|
9758
9461
|
const match = xForExpression.match(/^(\w+)\s+in\s+/);
|
|
9759
9462
|
return match ? match[1] : null;
|
|
9760
9463
|
}
|
|
9761
9464
|
|
|
9762
9465
|
Alpine.directive('files', (el, { expression, modifiers }, { effect, evaluateLater, cleanup }) => {
|
|
9763
9466
|
|
|
9764
|
-
//
|
|
9765
|
-
// If expression is a plain identifier (no quotes, no dots, no special chars), treat as string
|
|
9467
|
+
// A bare identifier is a data-source name (string literal), not an expression
|
|
9766
9468
|
const isPlainIdentifier = expression && /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(expression.trim());
|
|
9767
9469
|
|
|
9768
9470
|
const evaluate = isPlainIdentifier
|
|
@@ -9789,19 +9491,16 @@ function registerFilesDirective() {
|
|
|
9789
9491
|
let isProcessing = false; // Guard to prevent infinite loops
|
|
9790
9492
|
let waitingForDataSource = null; // Track which data source we're waiting for
|
|
9791
9493
|
|
|
9792
|
-
//
|
|
9793
|
-
//
|
|
9494
|
+
// Seed scope state synchronously (before Alpine evaluates expressions),
|
|
9495
|
+
// not in an effect, so the variables exist on first evaluation.
|
|
9794
9496
|
let scope;
|
|
9795
9497
|
try {
|
|
9796
9498
|
scope = Alpine.$data(el);
|
|
9797
9499
|
} catch (e) {
|
|
9798
|
-
|
|
9799
|
-
scope = null;
|
|
9500
|
+
scope = null; // not ready — seed in the effect instead
|
|
9800
9501
|
}
|
|
9801
9502
|
|
|
9802
9503
|
if (scope) {
|
|
9803
|
-
// Always initialize - don't check for undefined, just set them
|
|
9804
|
-
// This ensures they exist when Alpine first evaluates expressions
|
|
9805
9504
|
if (!('files' in scope)) {
|
|
9806
9505
|
scope.files = [];
|
|
9807
9506
|
}
|
|
@@ -10201,12 +9900,8 @@ function registerFilesDirective() {
|
|
|
10201
9900
|
});
|
|
10202
9901
|
});
|
|
10203
9902
|
|
|
10204
|
-
//
|
|
10205
|
-
//
|
|
10206
|
-
// Register x-entry-files directive - generic directive for displaying files linked to any table entry
|
|
10207
|
-
// Usage: <div x-entry-files="entry"> - automatically provides files, loadingFiles, filesError
|
|
10208
|
-
// Renamed from x-project-files to be more generic (works with any table entry, not just projects)
|
|
10209
|
-
// WeakMap to store namespace per element - ensures complete isolation
|
|
9903
|
+
// x-data-files="entry": provides files/loadingFiles/filesError for a table entry.
|
|
9904
|
+
// Per-element namespace via WeakMap for full isolation.
|
|
10210
9905
|
const dataFilesNamespaces = new WeakMap();
|
|
10211
9906
|
|
|
10212
9907
|
Alpine.directive('data-files', (el, { expression }, { effect, evaluateLater, cleanup }) => {
|
|
@@ -10226,10 +9921,9 @@ function registerFilesDirective() {
|
|
|
10226
9921
|
|
|
10227
9922
|
const directiveInstanceId = `directive-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
|
10228
9923
|
|
|
10229
|
-
// Isolated reactive scope for this
|
|
10230
|
-
// re-reads
|
|
10231
|
-
//
|
|
10232
|
-
// it — addScopeToNode attaches a fresh scope layer to this node.
|
|
9924
|
+
// Isolated reactive scope layer for this instance. Editing x-data won't
|
|
9925
|
+
// work (Alpine never re-reads it on initialized nodes) and would pollute
|
|
9926
|
+
// the ancestor scope — addScopeToNode attaches a fresh layer instead.
|
|
10233
9927
|
const isolatedData = Alpine.reactive({
|
|
10234
9928
|
files: [],
|
|
10235
9929
|
loadingFiles: false,
|
|
@@ -10238,8 +9932,7 @@ function registerFilesDirective() {
|
|
|
10238
9932
|
const removeIsolatedScope = Alpine.addScopeToNode(el, isolatedData);
|
|
10239
9933
|
cleanupCallbacks.push(removeIsolatedScope);
|
|
10240
9934
|
|
|
10241
|
-
// Merged
|
|
10242
|
-
// isolatedData (top of stack), magics like $watch resolve from ancestors
|
|
9935
|
+
// Merged view: files/* writes hit isolatedData (top of stack); $watch etc. resolve from ancestors
|
|
10243
9936
|
const scope = Alpine.$data(el);
|
|
10244
9937
|
|
|
10245
9938
|
// Store reference in WeakMap for access in closures
|
|
@@ -10395,17 +10088,13 @@ function registerFilesDirective() {
|
|
|
10395
10088
|
// CRITICAL DEBUG: Verify we're using the correct projectId before calling getFilesForEntry
|
|
10396
10089
|
const loadedFiles = await getFilesForEntry('projects', currentProjectId, bucketId, 'fileIds') || [];
|
|
10397
10090
|
|
|
10398
|
-
//
|
|
10399
|
-
// Don't mutate the existing array - create a completely new one
|
|
10091
|
+
// Fresh array so instances don't share references
|
|
10400
10092
|
const newFilesArray = [...loadedFiles];
|
|
10401
10093
|
|
|
10402
|
-
// Update local reference
|
|
10403
10094
|
files = newFilesArray;
|
|
10404
10095
|
|
|
10405
|
-
// Update both namespace and scope directly (they reference the same arrays/values)
|
|
10406
10096
|
const ns = getNamespace();
|
|
10407
10097
|
if (ns) {
|
|
10408
|
-
// DIAGNOSTIC: Mark each file with the project ID for tracking
|
|
10409
10098
|
const markedFiles = newFilesArray.map(file => ({
|
|
10410
10099
|
...file,
|
|
10411
10100
|
_debugProjectId: currentProjectId,
|
|
@@ -10433,13 +10122,11 @@ function registerFilesDirective() {
|
|
|
10433
10122
|
}
|
|
10434
10123
|
|
|
10435
10124
|
|
|
10436
|
-
//
|
|
10437
|
-
// This is more reliable than reading from the store, which might be stale
|
|
10125
|
+
// Track what we actually loaded (Appwrite is truth, store may be stale)
|
|
10438
10126
|
const loadedFileIdsArray = loadedFiles.map(f => f.$id) || [];
|
|
10439
10127
|
lastFileIds = JSON.stringify(loadedFileIdsArray);
|
|
10440
10128
|
|
|
10441
|
-
//
|
|
10442
|
-
// If the store/database has stale fileIds (file IDs that don't exist), clean them up
|
|
10129
|
+
// Reconcile stale fileIds in both the store and the Appwrite row
|
|
10443
10130
|
const store = Alpine.store('data');
|
|
10444
10131
|
const projects = store?.projects;
|
|
10445
10132
|
if (Array.isArray(projects)) {
|
|
@@ -10478,8 +10165,8 @@ function registerFilesDirective() {
|
|
|
10478
10165
|
}
|
|
10479
10166
|
}
|
|
10480
10167
|
|
|
10481
|
-
//
|
|
10482
|
-
//
|
|
10168
|
+
// Only write the DB if actually stale — avoids a
|
|
10169
|
+
// realtime event that could overwrite our store update
|
|
10483
10170
|
if (databaseIsStale) {
|
|
10484
10171
|
try {
|
|
10485
10172
|
const manifest = await window.ManifestDataConfig?.ensureManifest?.();
|
|
@@ -10549,10 +10236,7 @@ function registerFilesDirective() {
|
|
|
10549
10236
|
let fileIdsWatchUnwatch = null;
|
|
10550
10237
|
|
|
10551
10238
|
effect(() => {
|
|
10552
|
-
// Evaluate project expression - this will re-run when project changes
|
|
10553
10239
|
evaluateEntry((value) => {
|
|
10554
|
-
// Always get the latest project reference from the store by ID
|
|
10555
|
-
// This prevents using stale references when the projects array updates
|
|
10556
10240
|
const currentProjectId = value?.$id;
|
|
10557
10241
|
|
|
10558
10242
|
if (!currentProjectId) {
|
|
@@ -10602,13 +10286,11 @@ function registerFilesDirective() {
|
|
|
10602
10286
|
loadProjectFiles();
|
|
10603
10287
|
}
|
|
10604
10288
|
|
|
10605
|
-
// Watch
|
|
10606
|
-
// This ensures we always get the latest project reference from the store
|
|
10607
|
-
// Only create watch if we don't already have one for this project ID AND haven't created one yet
|
|
10289
|
+
// Watch this project's fileIds via the store (one watch per project id)
|
|
10608
10290
|
if (currentProjectId && scope && typeof scope.$watch === 'function' && !fileIdsWatchUnwatch && !watchCreated) {
|
|
10609
|
-
watchCreated = true;
|
|
10610
|
-
const projectId = currentProjectId; //
|
|
10611
|
-
let isProcessing = false;
|
|
10291
|
+
watchCreated = true;
|
|
10292
|
+
const projectId = currentProjectId; // capture for closure
|
|
10293
|
+
let isProcessing = false;
|
|
10612
10294
|
|
|
10613
10295
|
// Initialize lastFileIds with current value from the store to prevent false positives
|
|
10614
10296
|
const store = Alpine.store('data');
|
|
@@ -10620,23 +10302,17 @@ function registerFilesDirective() {
|
|
|
10620
10302
|
}
|
|
10621
10303
|
}
|
|
10622
10304
|
|
|
10623
|
-
//
|
|
10624
|
-
//
|
|
10625
|
-
// IMPORTANT: Only access the specific project's fileIds to minimize reactivity
|
|
10305
|
+
// Function watch (more reliable than a string expr for nested store access);
|
|
10306
|
+
// reads only this project's fileIds to keep reactivity narrow.
|
|
10626
10307
|
fileIdsWatchUnwatch = scope.$watch(
|
|
10627
10308
|
() => {
|
|
10628
|
-
// Access the store - Alpine tracks this
|
|
10629
10309
|
const store = Alpine.store('data');
|
|
10630
10310
|
const projects = store?.projects;
|
|
10631
10311
|
if (!Array.isArray(projects)) return null;
|
|
10632
10312
|
|
|
10633
|
-
// Find the current project by ID - always gets latest reference
|
|
10634
10313
|
const currentProject = projects.find(p => p.$id === projectId);
|
|
10635
10314
|
if (!currentProject) return null;
|
|
10636
10315
|
|
|
10637
|
-
// Return the fileIds array as JSON string for comparison
|
|
10638
|
-
// Accessing fileIds here makes Alpine track changes to this property
|
|
10639
|
-
// This should only trigger when THIS project's fileIds changes
|
|
10640
10316
|
return JSON.stringify(currentProject.fileIds || []);
|
|
10641
10317
|
},
|
|
10642
10318
|
(currentFileIdsJson) => {
|
|
@@ -10703,16 +10379,13 @@ function registerFilesDirective() {
|
|
|
10703
10379
|
};
|
|
10704
10380
|
|
|
10705
10381
|
const handleFileCreated = (e) => {
|
|
10706
|
-
// Check if the file is linked to this project
|
|
10707
10382
|
const fileId = e.detail?.fileId;
|
|
10708
10383
|
const entryId = e.detail?.entryId;
|
|
10709
10384
|
const tableName = e.detail?.tableName;
|
|
10710
10385
|
|
|
10711
|
-
//
|
|
10712
|
-
// This is more reliable than checking the store, which might be stale
|
|
10386
|
+
// Match on the event's entryId (store may be stale)
|
|
10713
10387
|
const matchesProject = entryId === projectId && tableName === 'projects';
|
|
10714
10388
|
|
|
10715
|
-
// Use entryId from event instead of checking store (which is stale)
|
|
10716
10389
|
if (fileId && matchesProject && !eventProcessing) {
|
|
10717
10390
|
eventProcessing = true;
|
|
10718
10391
|
// Reload files for this project - getFilesForEntry will get the latest from Appwrite
|
|
@@ -10764,8 +10437,7 @@ function registerFilesDirective() {
|
|
|
10764
10437
|
});
|
|
10765
10438
|
}
|
|
10766
10439
|
|
|
10767
|
-
//
|
|
10768
|
-
// Auto-register directive when Alpine is ready
|
|
10440
|
+
// Auto-registration handled elsewhere; kept for reference.
|
|
10769
10441
|
// if (typeof document !== 'undefined') {
|
|
10770
10442
|
// if (typeof Alpine !== 'undefined') {
|
|
10771
10443
|
// registerFilesDirective();
|
|
@@ -10778,9 +10450,8 @@ function registerFilesDirective() {
|
|
|
10778
10450
|
|
|
10779
10451
|
/* Manifest Data Sources - Main Initialization */
|
|
10780
10452
|
|
|
10781
|
-
//
|
|
10782
|
-
//
|
|
10783
|
-
// to match database team scope behavior
|
|
10453
|
+
// Client-side scope filter: Appwrite returns all accessible files, so narrow to
|
|
10454
|
+
// the current team to match database team-scope behavior.
|
|
10784
10455
|
async function filterFilesByScope(files, scope) {
|
|
10785
10456
|
if (!files || !Array.isArray(files) || files.length === 0) {
|
|
10786
10457
|
return files;
|
|
@@ -11035,26 +10706,20 @@ async function handleStorageRealtimeEvent(dataSourceName, bucketId, scope, event
|
|
|
11035
10706
|
}
|
|
11036
10707
|
}
|
|
11037
10708
|
|
|
11038
|
-
//
|
|
11039
|
-
// Use a combination of event type, ID, and timestamp to create a unique key per client
|
|
10709
|
+
// Realtime event dedup: key by type+id+sequence(+timestamp)
|
|
11040
10710
|
const processedEvents = new Map(); // Map<eventKey, timestamp>
|
|
11041
|
-
const EVENT_DEDUP_WINDOW = 2000; //
|
|
10711
|
+
const EVENT_DEDUP_WINDOW = 2000; // ms
|
|
11042
10712
|
|
|
11043
|
-
// Generate a unique key for an event
|
|
11044
|
-
// For updates, we need to be more careful - use timestamp to allow same sequence from different sources
|
|
11045
10713
|
function getEventKey(eventType, payload) {
|
|
11046
10714
|
const id = payload?.$id || payload?.row?.$id || payload?.rowId || payload?.id || payload;
|
|
11047
10715
|
const sequence = payload?.$sequence || payload?.sequence;
|
|
11048
10716
|
const timestamp = payload?.$updatedAt || payload?.$createdAt || payload?.timestamp;
|
|
11049
10717
|
|
|
11050
|
-
//
|
|
11051
|
-
// This handles the case where multiple clients receive the same sequence number
|
|
10718
|
+
// Updates add timestamp so the same sequence from different clients isn't dropped
|
|
11052
10719
|
if (eventType === 'update' && timestamp) {
|
|
11053
|
-
// Use a combination that allows same sequence from different times
|
|
11054
10720
|
return `${eventType}:${id}:${sequence}:${timestamp}`;
|
|
11055
10721
|
}
|
|
11056
10722
|
|
|
11057
|
-
// For create/delete, sequence is usually unique enough
|
|
11058
10723
|
return `${eventType}:${id}:${sequence || Date.now()}`;
|
|
11059
10724
|
}
|
|
11060
10725
|
|
|
@@ -11155,8 +10820,8 @@ async function handleTableRealtimeEvent(dataSourceName, databaseId, tableId, sco
|
|
|
11155
10820
|
// Check if row still matches scope after update
|
|
11156
10821
|
const rowMatchesScope = await checkRowMatchesScope(row, scope);
|
|
11157
10822
|
if (rowMatchesScope) {
|
|
11158
|
-
//
|
|
11159
|
-
//
|
|
10823
|
+
// Most sources update unconditionally; projects-with-fileIds get
|
|
10824
|
+
// special handling to protect optimistic uploads (see below).
|
|
11160
10825
|
const existingFileIds = existingRow?.fileIds || [];
|
|
11161
10826
|
const incomingFileIds = row?.fileIds || [];
|
|
11162
10827
|
const hasFileIds = existingFileIds.length > 0 || incomingFileIds.length > 0;
|
|
@@ -11164,41 +10829,30 @@ async function handleTableRealtimeEvent(dataSourceName, databaseId, tableId, sco
|
|
|
11164
10829
|
|
|
11165
10830
|
let shouldUpdate = true;
|
|
11166
10831
|
|
|
11167
|
-
// Special handling only for projects with fileIds
|
|
11168
10832
|
if (isProjectWithFiles) {
|
|
11169
10833
|
const existingUpdatedAt = existingRow?.$updatedAt || existingRow?.$sequence;
|
|
11170
10834
|
const newUpdatedAt = row?.$updatedAt || row?.$sequence;
|
|
11171
10835
|
const shouldUpdateByTimestamp = !newUpdatedAt || !existingUpdatedAt || newUpdatedAt !== existingUpdatedAt;
|
|
11172
10836
|
|
|
11173
10837
|
if (!shouldUpdateByTimestamp) {
|
|
11174
|
-
|
|
11175
|
-
shouldUpdate = false;
|
|
10838
|
+
shouldUpdate = false; // identical timestamps → no-op
|
|
11176
10839
|
} else {
|
|
11177
10840
|
const existingFileIdsSet = new Set(existingFileIds);
|
|
11178
10841
|
const incomingFileIdsSet = new Set(incomingFileIds);
|
|
11179
10842
|
|
|
11180
|
-
// Check if incoming is a superset (has all existing files + more)
|
|
11181
10843
|
const isSuperset = incomingFileIds.every(id => existingFileIdsSet.has(id)) &&
|
|
11182
10844
|
incomingFileIds.length > existingFileIds.length;
|
|
11183
10845
|
|
|
11184
|
-
// Check if incoming is missing files that exist in current (stale data)
|
|
11185
10846
|
const isMissingFiles = existingFileIds.some(id => !incomingFileIdsSet.has(id));
|
|
11186
10847
|
|
|
11187
|
-
// Only update if:
|
|
11188
|
-
// 1. Incoming is a superset (has all existing + more), OR
|
|
11189
|
-
// 2. Incoming is equal (same files), OR
|
|
11190
|
-
// 3. Timestamp comparison suggests it's definitely newer (more than 1 second difference)
|
|
11191
10848
|
const timestampDiff = newUpdatedAt > existingUpdatedAt ?
|
|
11192
10849
|
(new Date(newUpdatedAt) - new Date(existingUpdatedAt)) :
|
|
11193
10850
|
(new Date(existingUpdatedAt) - new Date(newUpdatedAt));
|
|
11194
|
-
const isDefinitelyNewer = timestampDiff > 1000; //
|
|
10851
|
+
const isDefinitelyNewer = timestampDiff > 1000; // ms
|
|
11195
10852
|
|
|
11196
|
-
//
|
|
11197
|
-
//
|
|
11198
|
-
// Protect optimistic updates by requiring incoming to be a superset or definitely newer
|
|
10853
|
+
// Incoming missing files + not clearly newer = stale event
|
|
10854
|
+
// racing an optimistic update; ignore it to protect the upload.
|
|
11199
10855
|
if (isMissingFiles && !isDefinitelyNewer) {
|
|
11200
|
-
// Incoming data is missing files and timestamp isn't definitely newer - likely stale
|
|
11201
|
-
// This protects optimistic updates from being overwritten by stale realtime events
|
|
11202
10856
|
console.warn('[Realtime] Ignoring stale realtime update (protecting optimistic update):', {
|
|
11203
10857
|
projectId: row.$id,
|
|
11204
10858
|
existingFileIds: existingFileIds,
|
|
@@ -11447,13 +11101,9 @@ async function loadDataSource(dataSourceName, locale = 'en') {
|
|
|
11447
11101
|
const cacheKey = `${dataSourceName}:${locale}`;
|
|
11448
11102
|
const { dataSourceCache, loadingPromises, isInitializing, updateStore } = window.ManifestDataStore;
|
|
11449
11103
|
|
|
11450
|
-
//
|
|
11451
|
-
//
|
|
11452
|
-
//
|
|
11453
|
-
// reload already wrote `name:fr` — serving the cached data is fine, but
|
|
11454
|
-
// writing it to the live store would clobber the current locale. Localized
|
|
11455
|
-
// data carries a `_locale` stamp; unstamped (non-localized) data writes
|
|
11456
|
-
// unconditionally.
|
|
11104
|
+
// Memory cache. Serving cached data is fine, but writing an old locale to the
|
|
11105
|
+
// live store would clobber a concurrent locale switch — so guard the store
|
|
11106
|
+
// write via the `_locale` stamp (unstamped/non-localized data writes freely).
|
|
11457
11107
|
if (dataSourceCache.has(cacheKey)) {
|
|
11458
11108
|
const cachedData = dataSourceCache.get(cacheKey);
|
|
11459
11109
|
const liveLocale = (window.Alpine && Alpine.store('locale')?.current)
|
|
@@ -11724,16 +11374,13 @@ async function loadDataSource(dataSourceName, locale = 'en') {
|
|
|
11724
11374
|
enhancedData = [];
|
|
11725
11375
|
}
|
|
11726
11376
|
|
|
11727
|
-
//
|
|
11728
|
-
// Always safe — the cache key carries the locale this load was for.
|
|
11377
|
+
// Cache is always safe — the key carries this load's locale.
|
|
11729
11378
|
dataSourceCache.set(cacheKey, enhancedData);
|
|
11730
11379
|
|
|
11731
|
-
// Stale-locale guard: if the
|
|
11732
|
-
//
|
|
11733
|
-
//
|
|
11734
|
-
//
|
|
11735
|
-
// locale-independent and must still write (the listener never reloads
|
|
11736
|
-
// them, so skipping would orphan an initial load).
|
|
11380
|
+
// Stale-locale guard: if the locale changed mid-load, a localized
|
|
11381
|
+
// source's result is stale (the locale listener already reloaded the
|
|
11382
|
+
// right one). Non-localized sources still write — the listener never
|
|
11383
|
+
// reloads them, so skipping would orphan the initial load.
|
|
11737
11384
|
const localeSensitive = !!(dataSource && typeof dataSource === 'object'
|
|
11738
11385
|
&& (dataSource.locales || dataSource[locale]));
|
|
11739
11386
|
const liveLocale = (window.Alpine && Alpine.store('locale')?.current)
|
|
@@ -11783,8 +11430,7 @@ async function loadDataSource(dataSourceName, locale = 'en') {
|
|
|
11783
11430
|
function setupUrlChangeListeners() {
|
|
11784
11431
|
let currentUrl = window.location.pathname;
|
|
11785
11432
|
|
|
11786
|
-
//
|
|
11787
|
-
// This is separate from the store to ensure Alpine tracks it properly
|
|
11433
|
+
// Separate reactive object for route tracking (Alpine tracks it reliably)
|
|
11788
11434
|
const routeTracker = Alpine.reactive ? Alpine.reactive({
|
|
11789
11435
|
currentUrl: window.location.pathname
|
|
11790
11436
|
}) : { currentUrl: window.location.pathname };
|
|
@@ -11800,22 +11446,16 @@ function setupUrlChangeListeners() {
|
|
|
11800
11446
|
currentUrl = newUrl;
|
|
11801
11447
|
const store = Alpine.store('data');
|
|
11802
11448
|
if (store && store._initialized) {
|
|
11803
|
-
//
|
|
11804
|
-
// CRITICAL: Use Object.assign or spread to ensure Alpine tracks the change
|
|
11805
|
-
// Direct property assignment might not trigger reactivity in all cases
|
|
11449
|
+
// Use Object.assign so Alpine reliably tracks the change
|
|
11806
11450
|
Object.assign(store, { _currentUrl: newUrl });
|
|
11807
11451
|
|
|
11808
|
-
//
|
|
11809
|
-
// This ensures Alpine tracks the change even if store access isn't tracked in Proxy get traps
|
|
11452
|
+
// Also update the reactive tracker (covers untracked store get traps)
|
|
11810
11453
|
if (routeTracker) {
|
|
11811
11454
|
routeTracker.currentUrl = newUrl;
|
|
11812
11455
|
}
|
|
11813
11456
|
|
|
11814
|
-
|
|
11815
|
-
//
|
|
11816
|
-
const _ = store._currentUrl;
|
|
11817
|
-
// Dispatch a custom event to ensure any components listening for URL changes can react
|
|
11818
|
-
// This helps with reactivity in cases where Alpine's automatic tracking might miss the update
|
|
11457
|
+
const _ = store._currentUrl; // touch to force tracking
|
|
11458
|
+
// Event fallback for cases Alpine's auto-tracking might miss
|
|
11819
11459
|
try {
|
|
11820
11460
|
window.dispatchEvent(new CustomEvent('manifest:data-url-change', {
|
|
11821
11461
|
detail: { url: newUrl }
|
|
@@ -11836,7 +11476,7 @@ function setupUrlChangeListeners() {
|
|
|
11836
11476
|
window.addEventListener('manifest:route-change', (event) => {
|
|
11837
11477
|
const newUrl = event.detail?.to ?? window.ManifestRoutingNavigation?.getCurrentRoute?.() ?? window.location.pathname;
|
|
11838
11478
|
updateCurrentUrl(newUrl);
|
|
11839
|
-
// Flush route proxies after other listeners
|
|
11479
|
+
// Flush route proxies after other listeners queue updates, so $x.*.$route() refreshes in place
|
|
11840
11480
|
setTimeout(() => {
|
|
11841
11481
|
if (window.ManifestDataRouteProxyUpdateQueue?.flushSync) {
|
|
11842
11482
|
window.ManifestDataRouteProxyUpdateQueue.flushSync();
|
|
@@ -11952,24 +11592,11 @@ async function initializeDataSourcesPlugin() {
|
|
|
11952
11592
|
_currentUrl: existingStore._currentUrl || window.location.pathname
|
|
11953
11593
|
});
|
|
11954
11594
|
|
|
11955
|
-
// Pre-load
|
|
11956
|
-
//
|
|
11957
|
-
//
|
|
11958
|
-
//
|
|
11959
|
-
//
|
|
11960
|
-
// - Localized objects (e.g. { "en": "...", "fr": "..." }) → only the
|
|
11961
|
-
// current locale file is fetched (+ default locale for fallback
|
|
11962
|
-
// merging if different), NOT all 35 variants.
|
|
11963
|
-
// - Single CSV with embedded locales → one file.
|
|
11964
|
-
//
|
|
11965
|
-
// Skipped (remain on-demand):
|
|
11966
|
-
// - Appwrite collections / buckets — require auth/session context.
|
|
11967
|
-
// - API-URL sources — may have side-effects or auth requirements.
|
|
11968
|
-
// - The special "manifest" key — handled separately below.
|
|
11969
|
-
//
|
|
11970
|
-
// Without pre-loading, sources like $x.clients load asynchronously on
|
|
11971
|
-
// first access, causing a visible flash in the SPA and missing data in
|
|
11972
|
-
// prerender snapshots.
|
|
11595
|
+
// Pre-load local file-backed sources so $x.* has real data on the first
|
|
11596
|
+
// render pass (avoids SPA flash + missing prerender data). At most one
|
|
11597
|
+
// fetch each (localized objects load only the current + default locale).
|
|
11598
|
+
// Skipped/on-demand: Appwrite (needs auth), API-URL (side-effects), and
|
|
11599
|
+
// the special "manifest" key (handled separately below).
|
|
11973
11600
|
try {
|
|
11974
11601
|
const manifest = await window.ManifestDataConfig.ensureManifest();
|
|
11975
11602
|
const locale = (typeof document !== 'undefined' && document.documentElement?.lang) || (typeof Alpine !== 'undefined' && Alpine.store('locale')?.current) || 'en';
|