mnfst 0.5.204 → 0.5.206
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.data.js +90 -40
- package/lib/manifest.data.js +95 -23
- package/lib/manifest.integrity.json +4 -4
- package/lib/manifest.schema.json +14 -0
- package/lib/manifest.sw.js +1 -1
- package/lib/manifest.utilities.js +22 -2
- package/lib/manifest.utilities.node.mjs +10 -3
- package/package.json +1 -1
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
(function () {
|
|
4
4
|
|
|
5
|
-
const MANIFEST_BUILD_VERSION = '0.5.
|
|
5
|
+
const MANIFEST_BUILD_VERSION = '0.5.206';
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
//
|
|
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
|
-
|
|
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
|
-
|
|
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',
|
|
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',
|
|
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
|
|
1067
|
-
|
|
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
|
-
//
|
|
1077
|
-
|
|
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',
|
|
1130
|
+
scopeQueries.push(['equal', cols.user, userId]);
|
|
1086
1131
|
} else {
|
|
1087
|
-
//
|
|
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',
|
|
1150
|
+
scopeQueries.push(['equal', cols.team, teamId]);
|
|
1106
1151
|
} else {
|
|
1107
|
-
// No team
|
|
1108
|
-
|
|
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',
|
|
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',
|
|
1170
|
+
const equalQueries = teamIds.map(id => ['equal', cols.team, id]);
|
|
1126
1171
|
scopeQueries.push(['or', equalQueries]);
|
|
1127
1172
|
}
|
|
1128
1173
|
} else {
|
|
1129
|
-
// No teams
|
|
1130
|
-
|
|
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
|
-
|
|
1209
|
-
|
|
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
|
|
package/lib/manifest.data.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
(function () {
|
|
4
4
|
|
|
5
|
-
const MANIFEST_BUILD_VERSION = '0.5.
|
|
5
|
+
const MANIFEST_BUILD_VERSION = '0.5.206';
|
|
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.
|
|
7521
|
-
enrichedData.
|
|
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.
|
|
7529
|
-
enrichedData.
|
|
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.
|
|
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.
|
|
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.
|
|
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-
|
|
3
|
+
"manifest.appwrite.data.js": "sha384-YtuOZdT1+3S78f3Jdl+/ftV5SJOQ/8Cu3hF5e3Iy+MH1lDCGXeYFC0BtLgo9/Usc",
|
|
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-
|
|
14
|
+
"manifest.data.js": "sha384-zd6Q2sXFE38RWMcTdOZxpUrKSlLlldK5y6EtvT07CmwJBV8MudA//TU4ciFVixUo",
|
|
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-
|
|
31
|
+
"manifest.sw.js": "sha384-89AE0R7+SBoRt43gWDCzHht8dVsqVsO1QbE4qx2UOgKDqcIHXW3O6Jt8fki0AqsM",
|
|
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-
|
|
38
|
+
"manifest.utilities.js": "sha384-UMTRSgpNYGjdhQoGtu/gT7btTdrUkKfOzAnFi0NU70/d6jdQtu6RgDqiVwYxkxrp",
|
|
39
39
|
"manifest.virtual.js": "sha384-YwwmAsZNddajnAkLhBWtrK8edHMJOhD7cJkQi3qmm5o1Thg0wbtDU0dsfrCHKTIQ",
|
|
40
40
|
"manifest.js": "sha384-jH0veyUdnBMoGwHxgUQ9Yjs0UneDMI/fYIsg1U58YPZwriKJ6Rv/aoA7RbLepoGa"
|
|
41
41
|
}
|
package/lib/manifest.schema.json
CHANGED
|
@@ -144,6 +144,20 @@
|
|
|
144
144
|
{ "type": "array", "items": { "type": "string", "enum": ["user", "team"] } }
|
|
145
145
|
]
|
|
146
146
|
},
|
|
147
|
+
"scopeColumn": {
|
|
148
|
+
"description": "Column name(s) `scope` filters/writes on. Default teamId/userId. A string sets both; an object sets them independently.",
|
|
149
|
+
"oneOf": [
|
|
150
|
+
{ "type": "string" },
|
|
151
|
+
{
|
|
152
|
+
"type": "object",
|
|
153
|
+
"properties": {
|
|
154
|
+
"team": { "type": "string" },
|
|
155
|
+
"user": { "type": "string" }
|
|
156
|
+
},
|
|
157
|
+
"additionalProperties": false
|
|
158
|
+
}
|
|
159
|
+
]
|
|
160
|
+
},
|
|
147
161
|
"autoInjectUserId": { "type": "boolean" },
|
|
148
162
|
"autoInjectTeamId": { "type": "boolean" },
|
|
149
163
|
"queries": {
|
package/lib/manifest.sw.js
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
|
|
17
17
|
// Build stamps the published version here; unstamped (dev) falls back to the
|
|
18
18
|
// registration URL's `v`, then 'dev'.
|
|
19
|
-
const BUILD_VERSION = '0.5.
|
|
19
|
+
const BUILD_VERSION = '0.5.206';
|
|
20
20
|
const CACHE_PREFIX = 'mnfst-sw:';
|
|
21
21
|
const META_CACHE = CACHE_PREFIX + 'meta';
|
|
22
22
|
const SWR_CAP = 200;
|
|
@@ -1490,8 +1490,12 @@ TailwindCompiler.prototype.scanStaticClasses = async function () {
|
|
|
1490
1490
|
|
|
1491
1491
|
// Extract classes from HTML content
|
|
1492
1492
|
TailwindCompiler.prototype.extractClassesFromHTML = function (html, classSet) {
|
|
1493
|
-
//
|
|
1494
|
-
|
|
1493
|
+
// Literal class="..."/class='...' only — lookbehind excludes `:class=`
|
|
1494
|
+
// (Alpine binding), whose "class=" substring would otherwise match and
|
|
1495
|
+
// truncate at the literal's first internal quote, leaking a stray '{'.
|
|
1496
|
+
// Whitespace-only split below keeps every token Tailwind would accept
|
|
1497
|
+
// (variants, arbitrary values, leading '-', '!'); only x-/$ tokens drop.
|
|
1498
|
+
const classRegex = /(?<![:\w])class=["']([^"']+)["']/g;
|
|
1495
1499
|
let match;
|
|
1496
1500
|
|
|
1497
1501
|
while ((match = classRegex.exec(html)) !== null) {
|
|
@@ -3386,6 +3390,15 @@ TailwindCompiler.prototype.extractSelectorsFromCssText = function (cssText) {
|
|
|
3386
3390
|
const len = cssText.length;
|
|
3387
3391
|
let i = 0;
|
|
3388
3392
|
while (i < len) {
|
|
3393
|
+
if (cssText[i] === ' ' || cssText[i] === '\n' || cssText[i] === '\r' || cssText[i] === '\t') { i++; continue; } // else an @-rule after a newline reads as a selector
|
|
3394
|
+
// Skip inter-rule whitespace first — without this, whitespace before an
|
|
3395
|
+
// `@layer`/`@media` (e.g. after a preceding `@layer base, ...;` statement
|
|
3396
|
+
// or sibling rule) leaves `i` off the '@', so the block below never fires
|
|
3397
|
+
// and the whole nested block is swallowed whole as one bogus selector,
|
|
3398
|
+
// losing every class inside it (real Tailwind/compileUtilities output is
|
|
3399
|
+
// always `@layer theme {...}` followed by `@layer utilities {...}`).
|
|
3400
|
+
while (i < len && /\s/.test(cssText[i])) i++;
|
|
3401
|
+
if (i >= len) break;
|
|
3389
3402
|
if (cssText[i] === '/' && cssText[i + 1] === '*') {
|
|
3390
3403
|
const end = cssText.indexOf('*/', i + 2);
|
|
3391
3404
|
i = end === -1 ? len : end + 2;
|
|
@@ -3525,6 +3538,13 @@ TailwindCompiler.prototype.stripCoveredRulesFromCss = function (cssText) {
|
|
|
3525
3538
|
const len = text.length;
|
|
3526
3539
|
let i = 0;
|
|
3527
3540
|
while (i < len) {
|
|
3541
|
+
if (text[i] === ' ' || text[i] === '\n' || text[i] === '\r' || text[i] === '\t') { out.push(text[i]); i++; continue; }
|
|
3542
|
+
// Skip (but preserve) inter-rule whitespace before checking for an
|
|
3543
|
+
// at-rule — see extractSelectorsFromCssText for why this matters.
|
|
3544
|
+
const wsStart = i;
|
|
3545
|
+
while (i < len && /\s/.test(text[i])) i++;
|
|
3546
|
+
if (i > wsStart) out.push(text.slice(wsStart, i));
|
|
3547
|
+
if (i >= len) break;
|
|
3528
3548
|
if (text[i] === '/' && text[i + 1] === '*') {
|
|
3529
3549
|
const end = text.indexOf('*/', i + 2);
|
|
3530
3550
|
const stop = end === -1 ? len : end + 2;
|
|
@@ -924,8 +924,12 @@ TailwindCompiler.prototype.scanStaticClasses = async function () {
|
|
|
924
924
|
|
|
925
925
|
// Extract classes from HTML content
|
|
926
926
|
TailwindCompiler.prototype.extractClassesFromHTML = function (html, classSet) {
|
|
927
|
-
//
|
|
928
|
-
|
|
927
|
+
// Literal class="..."/class='...' only — lookbehind excludes `:class=`
|
|
928
|
+
// (Alpine binding), whose "class=" substring would otherwise match and
|
|
929
|
+
// truncate at the literal's first internal quote, leaking a stray '{'.
|
|
930
|
+
// Whitespace-only split below keeps every token Tailwind would accept
|
|
931
|
+
// (variants, arbitrary values, leading '-', '!'); only x-/$ tokens drop.
|
|
932
|
+
const classRegex = /(?<![:\w])class=["']([^"']+)["']/g;
|
|
929
933
|
let match;
|
|
930
934
|
|
|
931
935
|
while ((match = classRegex.exec(html)) !== null) {
|
|
@@ -3028,7 +3032,10 @@ export async function compileUtilities({ classes = [], themeCss = '', baseCss =
|
|
|
3028
3032
|
const manifestLayer = allUtilities ? `@layer utilities {\n${allUtilities}\n}` : '';
|
|
3029
3033
|
const tailwindLayer = await compileTailwindPass(uniqueClasses, tailwind);
|
|
3030
3034
|
|
|
3031
|
-
|
|
3035
|
+
const body = [manifestLayer, tailwindLayer].filter(Boolean).join('\n\n');
|
|
3036
|
+
if (!body) return '';
|
|
3037
|
+
// Layer order preamble: wins over a later `base` reset regardless of load order.
|
|
3038
|
+
return `@layer base, components, utilities;\n\n${body}`;
|
|
3032
3039
|
}
|
|
3033
3040
|
|
|
3034
3041
|
// `class:token=` (per-class conditional binding) and the `:class="..."`
|