mnfst 0.5.203 → 0.5.205

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,7 +2,7 @@
2
2
 
3
3
  (function () {
4
4
 
5
- const MANIFEST_BUILD_VERSION = '0.5.203';
5
+ const MANIFEST_BUILD_VERSION = '0.5.205';
6
6
 
7
7
  /* Manifest Data Sources - Appwrite Integration */
8
8
 
@@ -841,12 +841,15 @@ const ALLOWED_VARIABLES = [
841
841
  ];
842
842
 
843
843
  // Get auth store value safely
844
+ // Path is relative to the auth store itself (e.g. "currentTeam.$id", not "auth.currentTeam.$id").
845
+ // Supports optional-chaining syntax ("currentTeam?.$id") by normalizing "?." to "." — a
846
+ // missing intermediate value already returns null via the loop below.
844
847
  function getAuthValue(path) {
845
848
  try {
846
849
  const store = Alpine.store('auth');
847
850
  if (!store) return null;
848
851
 
849
- const parts = path.split('.');
852
+ const parts = path.replace(/\?\./g, '.').split('.');
850
853
  let value = store;
851
854
 
852
855
  for (const part of parts) {
@@ -893,7 +896,9 @@ function interpolateVariable(value) {
893
896
 
894
897
  // Check if it's a variable reference
895
898
  if (value.startsWith('$auth.')) {
896
- const path = value.substring(1); // Remove leading $
899
+ // Strip the '$auth.' prefix (not just '$') — Alpine.store('auth') IS the auth
900
+ // object, so a path starting with another 'auth' segment would never resolve.
901
+ const path = value.substring('$auth.'.length);
897
902
  return getAuthValue(path);
898
903
  } else if (value === '$locale.current') {
899
904
  return getLocaleValue();
@@ -914,6 +919,12 @@ function isVariable(str) {
914
919
  return str.includes('.') || ALLOWED_VARIABLES.includes(str);
915
920
  }
916
921
 
922
+ // Thrown by interpolateQuery/interpolateObject when a whitelisted $auth.
923
+ // variable hasn't resolved yet (auth store not settled) — buildQueries
924
+ // catches this and returns NOT_READY rather than sending a query with a
925
+ // null/undefined arg (Appwrite 400s on that) to the network.
926
+ const NOT_READY = Symbol('manifest-data-not-ready');
927
+
917
928
  // Interpolate variables in query array
918
929
  function interpolateQuery(query) {
919
930
  if (!Array.isArray(query) || query.length === 0) {
@@ -925,7 +936,9 @@ function interpolateQuery(query) {
925
936
  if (typeof arg === 'string' && isVariable(arg)) {
926
937
  // Check if it's in the whitelist
927
938
  if (ALLOWED_VARIABLES.includes(arg)) {
928
- return interpolateVariable(arg);
939
+ const value = interpolateVariable(arg);
940
+ if (value === null || value === undefined) throw NOT_READY;
941
+ return value;
929
942
  } else {
930
943
  console.warn(`[Manifest Data] Variable "${arg}" is not in whitelist. Allowed:`, ALLOWED_VARIABLES);
931
944
  // SECURITY: Return empty string for non-whitelisted variables to prevent injection
@@ -952,7 +965,9 @@ function interpolateObject(obj) {
952
965
  for (const [key, value] of Object.entries(obj)) {
953
966
  if (typeof value === 'string' && isVariable(value)) {
954
967
  if (ALLOWED_VARIABLES.includes(value)) {
955
- result[key] = interpolateVariable(value);
968
+ const resolved = interpolateVariable(value);
969
+ if (resolved === null || resolved === undefined) throw NOT_READY;
970
+ result[key] = resolved;
956
971
  } else {
957
972
  console.warn(`[Manifest Data] Variable "${value}" is not in whitelist`);
958
973
  // SECURITY: Return empty string for non-whitelisted variables to prevent injection
@@ -969,15 +984,57 @@ function interpolateObject(obj) {
969
984
  return result;
970
985
  }
971
986
 
972
- // Build queries with scope injection
987
+ // Default scope column names, overridable per data source via `scopeColumn`
988
+ const DEFAULT_SCOPE_COLUMNS = { team: 'teamId', user: 'userId' };
989
+
990
+ // Event-driven wait for the auth store's initial hydration — resolves immediately
991
+ // once already settled, otherwise waits for manifest:auth:initialized (capped so a
992
+ // misconfigured/absent auth plugin can't hang a read forever).
993
+ const AUTH_INIT_WAIT_MS = 3000;
994
+ function waitForAuthInit(authStore) {
995
+ if (!authStore || (authStore._initialized && authStore.isAuthenticated !== undefined)) {
996
+ return Promise.resolve();
997
+ }
998
+ if (typeof window === 'undefined') return Promise.resolve();
999
+ return new Promise(resolve => {
1000
+ let done = false;
1001
+ const timer = setTimeout(finish, AUTH_INIT_WAIT_MS);
1002
+ function finish() {
1003
+ if (done) return;
1004
+ done = true;
1005
+ clearTimeout(timer);
1006
+ window.removeEventListener('manifest:auth:initialized', finish);
1007
+ resolve();
1008
+ }
1009
+ window.addEventListener('manifest:auth:initialized', finish);
1010
+ });
1011
+ }
1012
+
1013
+ // Build queries with scope injection. Returns null ("not ready") instead of a
1014
+ // query array when a $auth. arg or a required scope value hasn't resolved yet
1015
+ // — callers must skip the read (never send a null/undefined/empty-equal arg to
1016
+ // Appwrite) and retry once auth settles (manifest:auth:initialized/teams-loaded/login).
973
1017
  // SECURITY: Scope queries are ALWAYS prepended to user queries to prevent bypass
974
- async function buildQueries(queriesConfig, scope) {
1018
+ // scopeColumns: { team, user } column names to filter/write on (default teamId/userId)
1019
+ async function buildQueries(queriesConfig, scope, scopeColumns) {
1020
+ const cols = { ...DEFAULT_SCOPE_COLUMNS, ...(scopeColumns || {}) };
975
1021
  if (!queriesConfig || !Array.isArray(queriesConfig)) {
976
1022
  return [];
977
1023
  }
978
1024
 
1025
+ // Wait for auth to settle before interpolating — a $auth. reference in a raw
1026
+ // user query (queries.default) needs this regardless of whether `scope` is set.
1027
+ const authStore = typeof Alpine !== 'undefined' ? Alpine.store('auth') : null;
1028
+ await waitForAuthInit(authStore);
1029
+
979
1030
  // Interpolate user-provided queries
980
- const userQueries = queriesConfig.map(query => interpolateQuery(query));
1031
+ let userQueries;
1032
+ try {
1033
+ userQueries = queriesConfig.map(query => interpolateQuery(query));
1034
+ } catch (e) {
1035
+ if (e === NOT_READY) return null;
1036
+ throw e;
1037
+ }
981
1038
 
982
1039
  // SECURITY: Build scope queries FIRST (they will be prepended)
983
1040
  // This ensures scope restrictions cannot be bypassed by user queries
@@ -999,17 +1056,6 @@ async function buildQueries(queriesConfig, scope) {
999
1056
  const hasTeamScope = scopeArray.includes('team');
1000
1057
  const hasTeamsScope = scopeArray.includes('teams');
1001
1058
 
1002
- // Wait for auth store to be initialized (shared for all scopes)
1003
- const authStore = typeof Alpine !== 'undefined' ? Alpine.store('auth') : null;
1004
- if (authStore && (!authStore._initialized || authStore.isAuthenticated === undefined)) {
1005
- let attempts = 0;
1006
- const maxAttempts = 10; // Wait up to 500ms (10 * 50ms)
1007
- while (attempts < maxAttempts && (!authStore._initialized || authStore.isAuthenticated === undefined)) {
1008
- await new Promise(resolve => setTimeout(resolve, 50));
1009
- attempts++;
1010
- }
1011
- }
1012
-
1013
1059
  // Special case: ["user", "team"] or ["user", "teams"] or ["teams", "user"] or ["team", "user"] - use OR logic
1014
1060
  // Show projects that belong to user OR current team OR any of their teams
1015
1061
  // Note: "team" (singular) means currentTeam, "teams" (plural) means all teams in user's teams array
@@ -1024,7 +1070,7 @@ async function buildQueries(queriesConfig, scope) {
1024
1070
  if (isAuthenticated && user) {
1025
1071
  const userId = getAuthValue('userId') || getAuthValue('user.$id') || getAuthValue('user.id') || user.$id || user.id;
1026
1072
  if (userId) {
1027
- orQueries.push(['equal', 'userId', userId]);
1073
+ orQueries.push(['equal', cols.user, userId]);
1028
1074
  } else {
1029
1075
  }
1030
1076
  } else {
@@ -1053,7 +1099,7 @@ async function buildQueries(queriesConfig, scope) {
1053
1099
  teamIds = [...new Set(teamIds)];
1054
1100
 
1055
1101
  teamIds.forEach(teamId => {
1056
- orQueries.push(['equal', 'teamId', teamId]);
1102
+ orQueries.push(['equal', cols.team, teamId]);
1057
1103
  });
1058
1104
 
1059
1105
  if (orQueries.length > 0) {
@@ -1063,8 +1109,9 @@ async function buildQueries(queriesConfig, scope) {
1063
1109
  scopeQueries.push(['or', orQueries]);
1064
1110
  }
1065
1111
  } else {
1066
- // No user or teams - return no results
1067
- scopeQueries.push(['equal', 'userId', '']);
1112
+ // No user or teams resolved — not ready (or genuinely unauthenticated):
1113
+ // skip the read rather than send an empty-equal; caller retries on an auth event.
1114
+ return null;
1068
1115
  }
1069
1116
  } else {
1070
1117
  // Handle user scope (when not combined with teams)
@@ -1073,23 +1120,21 @@ async function buildQueries(queriesConfig, scope) {
1073
1120
  const user = authStore?.user;
1074
1121
 
1075
1122
  if (!isAuthenticated || !user) {
1076
- // User is not authenticated - return no results
1077
- scopeQueries.push(['equal', 'userId', '']);
1078
- // SECURITY: Return early with scope query to prevent any data access
1079
- return scopeQueries;
1123
+ // Not (yet) authenticated — skip the read; caller retries on manifest:auth:login
1124
+ return null;
1080
1125
  }
1081
1126
 
1082
1127
  // Get user ID value
1083
1128
  const userId = getAuthValue('userId') || getAuthValue('user.$id') || getAuthValue('user.id') || user.$id || user.id;
1084
1129
  if (userId) {
1085
- scopeQueries.push(['equal', 'userId', userId]);
1130
+ scopeQueries.push(['equal', cols.user, userId]);
1086
1131
  } else {
1087
- // User is authenticated but userId not found - return no results
1088
- scopeQueries.push(['equal', 'userId', '']);
1132
+ // Authenticated but userId not found — not ready
1089
1133
  if (!window.__manifestDataDebugLogged) {
1090
1134
  window.__manifestDataDebugLogged = true;
1091
1135
  debugAuthStore();
1092
1136
  }
1137
+ return null;
1093
1138
  }
1094
1139
  }
1095
1140
 
@@ -1102,10 +1147,10 @@ async function buildQueries(queriesConfig, scope) {
1102
1147
  authStore?.currentTeam?.id;
1103
1148
 
1104
1149
  if (teamId) {
1105
- scopeQueries.push(['equal', 'teamId', teamId]);
1150
+ scopeQueries.push(['equal', cols.team, teamId]);
1106
1151
  } else {
1107
- // No team ID found - return no results
1108
- scopeQueries.push(['equal', 'teamId', '']);
1152
+ // No current team (yet) — skip the read; caller retries on manifest:auth:teams-loaded
1153
+ return null;
1109
1154
  }
1110
1155
  } else if (hasTeamsScope) {
1111
1156
  // Multi-team scope - use all teams user belongs to
@@ -1118,16 +1163,16 @@ async function buildQueries(queriesConfig, scope) {
1118
1163
  if (teamIds.length > 0) {
1119
1164
  if (teamIds.length === 1) {
1120
1165
  // Single team - use equal for efficiency
1121
- scopeQueries.push(['equal', 'teamId', teamIds[0]]);
1166
+ scopeQueries.push(['equal', cols.team, teamIds[0]]);
1122
1167
  } else {
1123
1168
  // Multiple teams - use Query.or() with multiple Query.equal() calls
1124
1169
  // Build: Query.or([Query.equal('teamId', id1), Query.equal('teamId', id2), ...])
1125
- const equalQueries = teamIds.map(id => ['equal', 'teamId', id]);
1170
+ const equalQueries = teamIds.map(id => ['equal', cols.team, id]);
1126
1171
  scopeQueries.push(['or', equalQueries]);
1127
1172
  }
1128
1173
  } else {
1129
- // No teams found - return no results
1130
- scopeQueries.push(['equal', 'teamId', '']);
1174
+ // No teams (yet) — skip the read; caller retries on manifest:auth:teams-loaded
1175
+ return null;
1131
1176
  }
1132
1177
  }
1133
1178
  }
@@ -1205,8 +1250,11 @@ function toAppwriteQuery(queryArray) {
1205
1250
  }
1206
1251
 
1207
1252
  // Build Appwrite queries from configuration
1208
- async function buildAppwriteQueries(queriesConfig, scope) {
1209
- const queries = await buildQueries(queriesConfig, scope);
1253
+ // scopeColumns: { team, user } column names (default teamId/userId) — see manifest.data.config.js getScopeColumns
1254
+ // Returns null ("not ready") straight through — see buildQueries.
1255
+ async function buildAppwriteQueries(queriesConfig, scope, scopeColumns) {
1256
+ const queries = await buildQueries(queriesConfig, scope, scopeColumns);
1257
+ if (queries === null) return null;
1210
1258
  return queries
1211
1259
  .map(query => toAppwriteQuery(query))
1212
1260
  .filter(query => query !== null);
@@ -1217,10 +1265,12 @@ window.ManifestDataQueries = {
1217
1265
  interpolateVariable,
1218
1266
  interpolateQuery,
1219
1267
  interpolateObject,
1268
+ getAuthValue,
1220
1269
  buildQueries,
1221
1270
  buildAppwriteQueries,
1222
1271
  toAppwriteQuery,
1223
- ALLOWED_VARIABLES
1272
+ ALLOWED_VARIABLES,
1273
+ DEFAULT_SCOPE_COLUMNS
1224
1274
  };
1225
1275
 
1226
1276
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  (function () {
4
4
 
5
- const MANIFEST_BUILD_VERSION = '0.5.203';
5
+ const MANIFEST_BUILD_VERSION = '0.5.205';
6
6
 
7
7
  /* Manifest Data Sources - Configuration */
8
8
 
@@ -224,6 +224,24 @@ function getScope(dataSource) {
224
224
  return dataSource?.scope || null;
225
225
  }
226
226
 
227
+ // Get scope column names for a data source (for query building + write-side auto-inject).
228
+ // dataSource.scopeColumn can be a string (applies to both team/user) or
229
+ // { team: "workspaceId", user: "ownerId" } to name them independently.
230
+ // Defaults to teamId/userId when unset.
231
+ function getScopeColumns(dataSource) {
232
+ const raw = dataSource?.scopeColumn;
233
+ if (raw && typeof raw === 'object') {
234
+ return {
235
+ team: raw.team || 'teamId',
236
+ user: raw.user || 'userId'
237
+ };
238
+ }
239
+ if (typeof raw === 'string' && raw) {
240
+ return { team: raw, user: raw };
241
+ }
242
+ return { team: 'teamId', user: 'userId' };
243
+ }
244
+
227
245
  // Get auto-injection config from data source
228
246
  // Controls whether userId/teamId are automatically injected on create
229
247
  function getAutoInjectConfig(dataSource) {
@@ -251,6 +269,7 @@ window.ManifestDataConfig = {
251
269
  getAppwriteTableId,
252
270
  getAppwriteBucketId,
253
271
  getScope,
272
+ getScopeColumns,
254
273
  getQueries,
255
274
  getAutoInjectConfig
256
275
  };
@@ -4486,10 +4505,15 @@ function attachArrayMethods(array, dataSourceName, reloadDataSource) {
4486
4505
  throw new Error(`[Manifest Data] Pagination is only supported for Appwrite data sources`);
4487
4506
  }
4488
4507
  const scope = window.ManifestDataConfig.getScope(dataSource);
4508
+ const scopeColumns = window.ManifestDataConfig.getScopeColumns(dataSource);
4489
4509
  const queriesConfig = window.ManifestDataConfig.getQueries(dataSource);
4490
4510
  const baseQueries = queriesConfig
4491
- ? await window.ManifestDataQueries.buildAppwriteQueries(queriesConfig.default || queriesConfig, scope)
4492
- : await window.ManifestDataQueries.buildAppwriteQueries([], scope);
4511
+ ? await window.ManifestDataQueries.buildAppwriteQueries(queriesConfig.default || queriesConfig, scope, scopeColumns)
4512
+ : await window.ManifestDataQueries.buildAppwriteQueries([], scope, scopeColumns);
4513
+
4514
+ if (baseQueries === null) {
4515
+ throw new Error(`[Manifest Data] "${dataSourceName}" pagination: auth not ready yet — retry after auth settles`);
4516
+ }
4493
4517
 
4494
4518
  if (methodName === '$first') {
4495
4519
  const limit = args[0] || 10;
@@ -4832,10 +4856,15 @@ function createArrayProxyWithRoute(arrayTarget, dataSourceName = null, reloadDat
4832
4856
 
4833
4857
  // Get base queries (from manifest or scope)
4834
4858
  const scope = window.ManifestDataConfig.getScope(dataSource);
4859
+ const scopeColumns = window.ManifestDataConfig.getScopeColumns(dataSource);
4835
4860
  const queriesConfig = window.ManifestDataConfig.getQueries(dataSource);
4836
4861
  const baseQueries = queriesConfig
4837
- ? await window.ManifestDataQueries.buildAppwriteQueries(queriesConfig.default || queriesConfig, scope)
4838
- : await window.ManifestDataQueries.buildAppwriteQueries([], scope);
4862
+ ? await window.ManifestDataQueries.buildAppwriteQueries(queriesConfig.default || queriesConfig, scope, scopeColumns)
4863
+ : await window.ManifestDataQueries.buildAppwriteQueries([], scope, scopeColumns);
4864
+
4865
+ if (baseQueries === null) {
4866
+ throw new Error(`[Manifest Data] "${dataSourceName}" pagination: auth not ready yet — retry after auth settles`);
4867
+ }
4839
4868
 
4840
4869
  if (key === '$first') {
4841
4870
  const limit = args[0] || 10;
@@ -7498,13 +7527,14 @@ function createAppwriteMethodsHandler(dataSourceName, reloadDataSource) {
7498
7527
  const tableId = window.ManifestDataConfig.getAppwriteTableId(dataSource);
7499
7528
  const bucketId = window.ManifestDataConfig.getAppwriteBucketId(dataSource);
7500
7529
  const scope = window.ManifestDataConfig.getScope(dataSource);
7530
+ const scopeColumns = window.ManifestDataConfig.getScopeColumns(dataSource);
7501
7531
 
7502
7532
  // Handle table operations (TablesDB)
7503
7533
  if (tableId) {
7504
7534
  if (method === '$create') {
7505
7535
  const [data, rowId] = args;
7506
7536
 
7507
- // Auto-inject userId and/or teamId based on scope and config
7537
+ // Auto-inject userId and/or teamId (or their configured scopeColumn) based on scope and config
7508
7538
  const autoInject = window.ManifestDataConfig.getAutoInjectConfig(dataSource);
7509
7539
  let enrichedData = { ...data };
7510
7540
  const authStore = typeof Alpine !== 'undefined' ? Alpine.store('auth') : null;
@@ -7517,16 +7547,16 @@ function createAppwriteMethodsHandler(dataSourceName, reloadDataSource) {
7517
7547
  // Inject userId if user scope is active and enabled
7518
7548
  if (autoInject.userId && hasUserScope && authStore?.isAuthenticated && authStore?.user) {
7519
7549
  const userId = authStore?.user?.$id || authStore?.user?.id || authStore?.userId;
7520
- if (userId && !enrichedData.userId) {
7521
- enrichedData.userId = userId;
7550
+ if (userId && !enrichedData[scopeColumns.user]) {
7551
+ enrichedData[scopeColumns.user] = userId;
7522
7552
  }
7523
7553
  }
7524
7554
 
7525
7555
  // Inject teamId if team scope is active and enabled
7526
7556
  if (autoInject.teamId && hasTeamScope) {
7527
7557
  const teamId = authStore?.currentTeam?.$id || authStore?.currentTeam?.id;
7528
- if (teamId && !enrichedData.teamId) {
7529
- enrichedData.teamId = teamId;
7558
+ if (teamId && !enrichedData[scopeColumns.team]) {
7559
+ enrichedData[scopeColumns.team] = teamId;
7530
7560
  }
7531
7561
  }
7532
7562
 
@@ -7838,8 +7868,12 @@ function createAppwriteMethodsHandler(dataSourceName, reloadDataSource) {
7838
7868
  const [queries] = args;
7839
7869
  const appwriteQueries = await window.ManifestDataQueries.buildAppwriteQueries(
7840
7870
  queries || [],
7841
- scope
7871
+ scope,
7872
+ scopeColumns
7842
7873
  );
7874
+ if (appwriteQueries === null) {
7875
+ throw new Error(`[Manifest Data] "${dataSourceName}" $query: auth not ready yet — retry after auth settles`);
7876
+ }
7843
7877
  // Dedupe key: source + serialized queries (never across different queries)
7844
7878
  const key = `${dataSourceName}:$query:${JSON.stringify(appwriteQueries.map(q => String(q)))}`;
7845
7879
  const { runDeduped, landRows } = window.ManifestDataStore || {};
@@ -8963,10 +8997,15 @@ function createPaginationMethod(methodName, dataSourceName) {
8963
8997
 
8964
8998
  // Base queries from manifest or scope
8965
8999
  const scope = window.ManifestDataConfig.getScope(dataSource);
9000
+ const scopeColumns = window.ManifestDataConfig.getScopeColumns(dataSource);
8966
9001
  const queriesConfig = window.ManifestDataConfig.getQueries(dataSource);
8967
9002
  const baseQueries = queriesConfig
8968
- ? await window.ManifestDataQueries.buildAppwriteQueries(queriesConfig.default || queriesConfig, scope)
8969
- : await window.ManifestDataQueries.buildAppwriteQueries([], scope);
9003
+ ? await window.ManifestDataQueries.buildAppwriteQueries(queriesConfig.default || queriesConfig, scope, scopeColumns)
9004
+ : await window.ManifestDataQueries.buildAppwriteQueries([], scope, scopeColumns);
9005
+
9006
+ if (baseQueries === null) {
9007
+ throw new Error(`[Manifest Data] "${dataSourceName}" pagination: auth not ready yet — retry after auth settles`);
9008
+ }
8970
9009
 
8971
9010
  if (methodName === '$first') {
8972
9011
  const limit = args[0] || 10;
@@ -11257,6 +11296,25 @@ function registerFilesDirective() {
11257
11296
 
11258
11297
  /* Manifest Data Sources - Main Initialization */
11259
11298
 
11299
+ // A scoped query that isn't ready yet (buildAppwriteQueries returned null — see
11300
+ // manifest.data.queries.js) skips the network read and waits here instead of
11301
+ // sending a broken/empty-equal query. Retries once when auth settles further;
11302
+ // dedup by key so a source pending across repeated loads doesn't stack listeners.
11303
+ const pendingAuthRetries = new Set();
11304
+ const AUTH_RETRY_EVENTS = ['manifest:auth:initialized', 'manifest:auth:teams-loaded', 'manifest:auth:login'];
11305
+ function scheduleAuthRetry(dataSourceName, locale) {
11306
+ if (typeof window === 'undefined') return;
11307
+ const key = `${dataSourceName}:${locale}`;
11308
+ if (pendingAuthRetries.has(key)) return;
11309
+ pendingAuthRetries.add(key);
11310
+ const retry = () => {
11311
+ pendingAuthRetries.delete(key);
11312
+ AUTH_RETRY_EVENTS.forEach(type => window.removeEventListener(type, retry));
11313
+ loadDataSource(dataSourceName, locale, { reload: true });
11314
+ };
11315
+ AUTH_RETRY_EVENTS.forEach(type => window.addEventListener(type, retry));
11316
+ }
11317
+
11260
11318
  // Client-side scope filter: Appwrite returns all accessible files, so narrow to
11261
11319
  // the current team to match database team-scope behavior.
11262
11320
  async function filterFilesByScope(files, scope) {
@@ -11459,7 +11517,7 @@ function markEventProcessed(eventKey) {
11459
11517
  }
11460
11518
 
11461
11519
  // Handle real-time events for database tables
11462
- async function handleTableRealtimeEvent(dataSourceName, databaseId, tableId, scope, eventType, payload) {
11520
+ async function handleTableRealtimeEvent(dataSourceName, databaseId, tableId, scope, scopeColumns, eventType, payload) {
11463
11521
 
11464
11522
  // Deduplicate events
11465
11523
  const eventKey = getEventKey(eventType, payload);
@@ -11486,7 +11544,7 @@ async function handleTableRealtimeEvent(dataSourceName, databaseId, tableId, sco
11486
11544
  if (eventType === 'create') {
11487
11545
  const row = payload?.$id ? payload : (payload?.row || payload);
11488
11546
  if (row && row.$id) {
11489
- const rowMatchesScope = await checkRowMatchesScope(row, scope);
11547
+ const rowMatchesScope = await checkRowMatchesScope(row, scope, scopeColumns);
11490
11548
  if (rowMatchesScope) {
11491
11549
  landRows(dataSourceName, [row], { mode: 'append' });
11492
11550
  }
@@ -11497,7 +11555,7 @@ async function handleTableRealtimeEvent(dataSourceName, databaseId, tableId, sco
11497
11555
  const row = payload?.$id ? payload : (payload?.row || payload);
11498
11556
  if (row && row.$id) {
11499
11557
  const existingRow = currentRows.find(r => r.$id === row.$id);
11500
- const rowMatchesScope = await checkRowMatchesScope(row, scope);
11558
+ const rowMatchesScope = await checkRowMatchesScope(row, scope, scopeColumns);
11501
11559
  if (!rowMatchesScope) {
11502
11560
  // Row no longer matches scope, remove it
11503
11561
  if (existingRow) landRemove(dataSourceName, [row.$id]);
@@ -11585,11 +11643,12 @@ async function handleTableRealtimeEvent(dataSourceName, databaseId, tableId, sco
11585
11643
  }
11586
11644
 
11587
11645
  // Check if a database row matches the current scope
11588
- async function checkRowMatchesScope(row, scope) {
11646
+ async function checkRowMatchesScope(row, scope, scopeColumns) {
11589
11647
  if (!scope || !row) {
11590
11648
  return true; // No scope, allow it
11591
11649
  }
11592
11650
 
11651
+ const cols = scopeColumns || { team: 'teamId', user: 'userId' };
11593
11652
  const authStore = typeof Alpine !== 'undefined' ? Alpine.store('auth') : null;
11594
11653
  if (!authStore) {
11595
11654
  return true;
@@ -11604,7 +11663,7 @@ async function checkRowMatchesScope(row, scope) {
11604
11663
  if (hasUserScope) {
11605
11664
  const user = authStore.user;
11606
11665
  const userId = user?.$id || user?.id;
11607
- if (userId && row.userId === userId) {
11666
+ if (userId && row[cols.user] === userId) {
11608
11667
  return true; // Matches user scope
11609
11668
  }
11610
11669
  }
@@ -11612,7 +11671,7 @@ async function checkRowMatchesScope(row, scope) {
11612
11671
  // Check team scope (singular - currentTeam)
11613
11672
  if (hasTeamScope) {
11614
11673
  const currentTeamId = authStore.currentTeam?.$id || authStore.currentTeam?.id;
11615
- if (currentTeamId && row.teamId === currentTeamId) {
11674
+ if (currentTeamId && row[cols.team] === currentTeamId) {
11616
11675
  return true; // Matches current team scope
11617
11676
  }
11618
11677
  }
@@ -11621,7 +11680,7 @@ async function checkRowMatchesScope(row, scope) {
11621
11680
  if (hasTeamsScope) {
11622
11681
  const teams = authStore.teams || [];
11623
11682
  const teamIds = teams.map(t => t.$id || t.id).filter(id => id);
11624
- if (teamIds.includes(row.teamId)) {
11683
+ if (teamIds.includes(row[cols.team])) {
11625
11684
  return true; // Matches one of user's teams
11626
11685
  }
11627
11686
  }
@@ -11826,13 +11885,20 @@ async function loadDataSource(dataSourceName, locale = 'en', options = {}) {
11826
11885
  const tableId = window.ManifestDataConfig.getAppwriteTableId(dataSource);
11827
11886
  // bucketId already defined above
11828
11887
  const scope = window.ManifestDataConfig.getScope(dataSource);
11888
+ const scopeColumns = window.ManifestDataConfig.getScopeColumns(dataSource);
11829
11889
  const queriesConfig = window.ManifestDataConfig.getQueries(dataSource);
11830
11890
 
11831
11891
  if (tableId) {
11832
11892
  // Load from Appwrite table (TablesDB)
11833
11893
  const queries = queriesConfig
11834
- ? await window.ManifestDataQueries.buildAppwriteQueries(queriesConfig.default || queriesConfig, scope)
11835
- : await window.ManifestDataQueries.buildAppwriteQueries([], scope);
11894
+ ? await window.ManifestDataQueries.buildAppwriteQueries(queriesConfig.default || queriesConfig, scope, scopeColumns)
11895
+ : await window.ManifestDataQueries.buildAppwriteQueries([], scope, scopeColumns);
11896
+
11897
+ // Not ready (auth/scope unresolved): skip this read, stay pending, retry on an auth event
11898
+ if (queries === null) {
11899
+ scheduleAuthRetry(dataSourceName, locale);
11900
+ return null;
11901
+ }
11836
11902
 
11837
11903
  // Log auth state for debugging
11838
11904
  const authStore = typeof Alpine !== 'undefined' ? Alpine.store('auth') : null;
@@ -11853,7 +11919,7 @@ async function loadDataSource(dataSourceName, locale = 'en', options = {}) {
11853
11919
  scope,
11854
11920
  async (eventType, payload) => {
11855
11921
  // Handle real-time events
11856
- await handleTableRealtimeEvent(dataSourceName, appwriteConfig.databaseId, tableId, scope, eventType, payload);
11922
+ await handleTableRealtimeEvent(dataSourceName, appwriteConfig.databaseId, tableId, scope, scopeColumns, eventType, payload);
11857
11923
  }
11858
11924
  );
11859
11925
  }
@@ -11866,6 +11932,12 @@ async function loadDataSource(dataSourceName, locale = 'en', options = {}) {
11866
11932
  ? await window.ManifestDataQueries.buildAppwriteQueries(queriesConfig.default || queriesConfig, null)
11867
11933
  : await window.ManifestDataQueries.buildAppwriteQueries([], null);
11868
11934
 
11935
+ // Not ready (a $auth. arg in queriesConfig unresolved): skip, retry on an auth event
11936
+ if (queries === null) {
11937
+ scheduleAuthRetry(dataSourceName, locale);
11938
+ return null;
11939
+ }
11940
+
11869
11941
  let files = await window.ManifestDataAppwrite.listBucketFiles(bucketId, queries);
11870
11942
 
11871
11943
  // Ensure files is always an array
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "manifest.appwrite.auth.js": "sha384-HMJmRXnvdDBWyuteXL+DTxWM2llSCdjl4EDamEI7iHNAVlqeOCE4GDTm8ZHipPRj",
3
- "manifest.appwrite.data.js": "sha384-GWqV1tsC6nmAReFVHu/Ks/0BGFrP/dx4kl1Us0p+c4V5JRzgt64+hRlkhqE03EKC",
3
+ "manifest.appwrite.data.js": "sha384-SNLZhDdZFodPsMINThHOZ/rPyLqV/hRyvI/lMxsHCctKB+TI4HdlLt9USnaKFKnG",
4
4
  "manifest.appwrite.presences.js": "sha384-JyVDWFT69b7Svq8J8p9tNWe2v9PZNQBY5UyhtOl1vnuHFAX0CRQ3MA9SiKq5wzNh",
5
5
  "manifest.bindings.js": "sha384-lPtMf8Ykaic9gNVZKq8Scia1881ZeMAyxer3WCIkUNwwrgzGBWto1OXADUwjvORF",
6
6
  "manifest.charts.js": "sha384-qQrjhWkIDi4oU5uQrpy8DdlMjY+gfiivBuX6mssNiFdkawgkVBvKneGgp+nd3PYJ",
@@ -11,7 +11,7 @@
11
11
  "manifest.combobox.js": "sha384-SN3XHumoebxcduFQqx5zt65/Cd10y8nuf9Gkco6UnsZ42sadRd+gvVLfWPdkkuPH",
12
12
  "manifest.components.js": "sha384-DLGSqCbnGkS87AWJ8GpKoAFP4nCosgC5SFSg6C10hH/nWu/MxI+0ntHlnFEfs++u",
13
13
  "manifest.computed.js": "sha384-GYML/e81gQfN+iDbqhPUgny8bINCyL+SrbTofoGk4hlCnLLjv51mCv42JMqq+ku5",
14
- "manifest.data.js": "sha384-9g/81jTkiYnBiRW3e1s0wxmtTTUYeGcrch3ry7ki7S/Gv6B3IMEtB7F9N4zk4U9H",
14
+ "manifest.data.js": "sha384-pCz9N7amtpL1KG/Z/3ZbGRPqof5K/pXwjK4NgB7MxBPg4efzFp0MlsQtJpjRnoNd",
15
15
  "manifest.datepicker.js": "sha384-8vTJYkLqqvgXgm1w1becuaRR/jUEpiLBjBJ6RsRbUr+ZkypvspD6EsssuDrX4SQq",
16
16
  "manifest.defer.js": "sha384-wXa60M2OHigi5rBurXQ7Oa8eyr5WTKs7EUn2GOru62KMdfs/LDyNegxCc9MNJFSe",
17
17
  "manifest.device.js": "sha384-g+txmrEszYtuOvRelTp/c1wSNR2AtllVI9rx5NPPD7DzhVUnMNughonZykv2xlBW",
@@ -28,14 +28,14 @@
28
28
  "manifest.slides.js": "sha384-3uRTkyK9XPLmnxI2+igZlpi4EyPlU/7IHj5j3BZJJ2KN455vXyk99fiXV3feO/XY",
29
29
  "manifest.status.js": "sha384-nwvUSbT+gpRMqhYx6MeBYswUMH72rY92mjgxv0vrSG7Ht7l83XYpEB6fCTbXjjfK",
30
30
  "manifest.svg.js": "sha384-qr/8H9+2cIhSXNbRWRtIdrDlTOxKlNJqwEefHvCYoiBNW/kKhPTFa109Orv/EBnb",
31
- "manifest.sw.js": "sha384-Te2cSGBZQmK79BME5Zg5i7Hle5ncBSvJFI94CiGduupVSuDdBBTUstBTXpqp/QgY",
31
+ "manifest.sw.js": "sha384-f+wBpbAvoE4WwJXkMiFJ4pN/UDVdsmEBX7Uav3SoycmUXzgZDz+U/39UWXtLaItm",
32
32
  "manifest.tabs.js": "sha384-OIIcyaQnGS0eNYk4zgAFEDboNYect7THvUd9D0V6nqCeDOxQcoiqLrBEmmWVTTet",
33
33
  "manifest.tailwind.js": "sha384-r/Jvqb+Piz21jUts10BbVAJoKkShOdJRVFpywyi4HPIi/OfOJq5CtPyWAPbdkMgt",
34
34
  "manifest.text.edit.js": "sha384-pfhc4IPKNJokj6ldlgD+KwGLN4UoM21CNeVf2sVq/34X1Un2PUhEuyTg5itmtsXS",
35
35
  "manifest.toasts.js": "sha384-/OV32LLMJl0Iw8NoNQJ+5lgntLxjIJq/YNj5JAKS/+nUhVx4MMB9h1FOqP7zuj2M",
36
36
  "manifest.tooltips.js": "sha384-uXVVQADl6pY2Pj9i/UaSclZN7dzbKhLkmevv7Ad0+wXR8LgqodALNFrc5jZJjlwm",
37
37
  "manifest.url.parameters.js": "sha384-J2aSVBOPsoqsbafHndrWLHEDpKb4AKJAoQnVmKA4gZh5P/8HHjFWHO/lkusmG84l",
38
- "manifest.utilities.js": "sha384-frvYhLP8Arx+smhzQr1nCvxuVfnjRhoG+ce8pI0H0iYqJwO7km1/3QLdBYt3uKol",
38
+ "manifest.utilities.js": "sha384-Dtpf9qskRUYB4bLuJaCBGWYFENVypV5AxlLFPdi7S7+meaGeupzD2zkDr7CYhvXf",
39
39
  "manifest.virtual.js": "sha384-YwwmAsZNddajnAkLhBWtrK8edHMJOhD7cJkQi3qmm5o1Thg0wbtDU0dsfrCHKTIQ",
40
- "manifest.js": "sha384-z6bjoNHfkJwD3bRcjChzuzM3y/3LrWVfc8nsApLtSXcshgJc8L4+Vz7XaV3MN6N8"
40
+ "manifest.js": "sha384-jH0veyUdnBMoGwHxgUQ9Yjs0UneDMI/fYIsg1U58YPZwriKJ6Rv/aoA7RbLepoGa"
41
41
  }