@gonvex/cli 0.5.2-staging.12 → 0.5.2-staging.14

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.
@@ -1,8 +1,9 @@
1
+ import { nativeLocalBindings } from "./native-local-bindings.js";
1
2
  import { rolldown } from "rolldown";
2
3
  import { createHash } from "node:crypto";
3
4
  import { readFile, readdir, writeFile, mkdir } from "node:fs/promises";
4
5
  import { dirname, join, relative, resolve } from "node:path";
5
- import { compileLocalSchema } from "@gonvex/local-runtime/schema";
6
+ import { compileLocalSchema } from "./local-schema.js";
6
7
  export async function projectLocalSchema(root) {
7
8
  const migrationDirectory = join(root, "migrations");
8
9
  const names = (await readdir(migrationDirectory).catch(() => [])).filter((name) => name.endsWith(".sql")).sort();
@@ -38,15 +39,44 @@ export async function localBindings(root, manifest) {
38
39
  await writeFile(entryFile, `${imports.join("\n")}\nexport const localReducers = {${reducers.map(([path], index) => `${JSON.stringify(path)}: reducer${index}`).join(",")}};`);
39
40
  const bundle = await rolldown({
40
41
  input: entryFile, cwd: root, platform: "browser", tsconfig: false,
41
- treeshake: { manualPureFunctions: ["action", "internalReducer", "query", "liveQuery", "replicaCollection", "visibility"] },
42
+ // These declarations describe server entry points. Their definitions and
43
+ // schema construction are unnecessary when only Reducer exports are used.
44
+ treeshake: { manualPureFunctions: ["action", "internalReducer", "query", "internalQuery", "liveQuery", "replicaCollection", "visibility", "cron", "tenantCron", "schema"] },
42
45
  resolve: { conditionNames: ["browser", "import", "default"], mainFields: ["browser", "module", "main"] },
43
46
  });
44
47
  let localCode;
48
+ const webChunks = {};
45
49
  try {
46
50
  const output = await bundle.generate({ format: "esm", codeSplitting: false });
47
51
  if (output.output.length !== 1 || output.output[0]?.type !== "chunk")
48
52
  throw new Error("Local Reducers must compile into one self-contained module");
49
53
  localCode = output.output[0].code;
54
+ // Browser controls preload their own Reducers. Unopened features keep
55
+ // their code on disk instead of importing every application module at boot.
56
+ const lazyEntry = join(root, 'gonvex', '_build', 'local-lazy-entry.ts');
57
+ const loaders = reducers.map(([path, entry]) => {
58
+ let file = relative(dirname(lazyEntry), resolve(root, entry.file)).replaceAll('\\', '/').replace(/\.(?:tsx?|mts|cts)$/, '.js');
59
+ if (!file.startsWith('.'))
60
+ file = `./${file}`;
61
+ return `${JSON.stringify(path)}: () => import(${JSON.stringify(file)}).then(module => module.${entry.handler})`;
62
+ });
63
+ await writeFile(lazyEntry, `export const localReducers = {${loaders.join(',')}};`);
64
+ const lazyBundle = await rolldown({ input: lazyEntry, cwd: root, platform: 'browser', tsconfig: false,
65
+ treeshake: { manualPureFunctions: ['action', 'internalReducer', 'query', 'internalQuery', 'liveQuery', 'replicaCollection', 'visibility', 'cron', 'tenantCron', 'schema'] },
66
+ resolve: { conditionNames: ['browser', 'import', 'default'], mainFields: ['browser', 'module', 'main'] },
67
+ });
68
+ let web;
69
+ try {
70
+ web = await lazyBundle.generate({ format: 'esm', entryFileNames: 'local-reducers.js', chunkFileNames: 'local-reducer-[name]-[hash].js' });
71
+ }
72
+ finally {
73
+ await lazyBundle.close();
74
+ }
75
+ for (const chunk of web.output) {
76
+ if (chunk.type !== "chunk")
77
+ throw new Error("Unexpected local Reducer asset");
78
+ webChunks[chunk.fileName] = chunk.code;
79
+ }
50
80
  }
51
81
  finally {
52
82
  await bundle.close();
@@ -54,12 +84,17 @@ export async function localBindings(root, manifest) {
54
84
  const contractBytes = manifest.module.files["client-contract.json"];
55
85
  const clientContract = contractBytes ? JSON.parse(Buffer.from(contractBytes, "base64").toString()) : undefined;
56
86
  const header = "// Generated by Gonvex. Do not edit.\n";
87
+ const native = await nativeLocalBindings(root, localCode, schema, {
88
+ clientContract, artifactHash: manifest.module.hash, tables: Object.keys(schema),
89
+ collections: Object.entries(manifest.functions).filter(([path]) => path.startsWith("__local.")).map(([path, entry]) => ({ kind: "query", path, delivery: "replica", replica: entry.replica })),
90
+ });
57
91
  return {
92
+ ...native,
58
93
  "local-schema.ts": `${header}export const localSchema = ${JSON.stringify(schema)} as const;\n`,
59
- "local-reducers.js": header + localCode,
60
- "local-reducers.d.ts": `${header}import type { ReducerDefinition } from "@gonvex/module-sdk";\nexport declare const localReducers: Readonly<Record<string, ReducerDefinition<any, any>>>;\n`,
61
- "local-worker.ts": `${header}import { serveLocalReducerWorker } from "@gonvex/local-runtime/worker";\nimport { localSchema } from "./local-schema.js";\nimport { localReducers } from "./local-reducers.js";\nserveLocalReducerWorker({ schema: localSchema, reducers: localReducers, artifactHash: ${JSON.stringify(manifest.module.hash)} });\n`,
62
- "local-runtime.ts": `${header}import { createLocalReducerWorker } from "@gonvex/local-runtime/worker-client";\nexport const localRuntime = { clientContract: ${JSON.stringify(clientContract)}, artifactHash: ${JSON.stringify(manifest.module.hash)}, tables: ${JSON.stringify(Object.keys(schema))}, collections: ${JSON.stringify(Object.entries(manifest.functions).filter(([path]) => path.startsWith("__local.")).map(([path, entry]) => ({ kind: "query", path, delivery: "replica", replica: entry.replica })))} as const, create: () => createLocalReducerWorker(new Worker(new URL("./local-worker.ts", import.meta.url), { type: "module" })) };\n`,
94
+ ...Object.fromEntries(Object.entries(webChunks).map(([name, code]) => [name, header + code])),
95
+ "local-reducers.d.ts": `${header}import type { ReducerDefinition } from "@gonvex/module-sdk";\nexport declare const localReducers: Readonly<Record<string, () => Promise<ReducerDefinition<any, any>>>>;\n`,
96
+ "local-runtime.ts": `${header}export {localRuntime} from "./local-executor.js";\n`,
97
+ "local-executor.ts": `${header}import { createPortableReducer } from "@gonvex/local-runtime/portable-client";\nimport { localSchema } from "./local-schema.js";\nimport { localReducers } from "./local-reducers.js";\nexport const localRuntime = { mode: "portable" as const, clientContract: ${JSON.stringify(clientContract)}, artifactHash: ${JSON.stringify(manifest.module.hash)}, tables: ${JSON.stringify(Object.keys(schema))}, collections: ${JSON.stringify(Object.entries(manifest.functions).filter(([path]) => path.startsWith("__local.")).map(([path, entry]) => ({ kind: "query", path, delivery: "replica", replica: entry.replica })))} as const, create: () => createPortableReducer({schema: localSchema, reducers: localReducers, artifactHash: ${JSON.stringify(manifest.module.hash)}}) };\n`,
63
98
  };
64
99
  }
65
100
  //# sourceMappingURL=local-bindings.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"local-bindings.js","sourceRoot":"","sources":["../src/local-bindings.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"local-bindings.js","sourceRoot":"","sources":["../src/local-bindings.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ import type { LocalSchema } from "@gonvex/local-runtime/schema";
2
+ /** Compile schema using PostgreSQL itself, without connecting to a service. */
3
+ export declare function compileLocalSchema(migrations: readonly {
4
+ name: string;
5
+ sql: string;
6
+ }[]): Promise<LocalSchema>;
@@ -0,0 +1,55 @@
1
+ import { PGlite } from "@electric-sql/pglite";
2
+ import { pg_trgm } from "@electric-sql/pglite/contrib/pg_trgm";
3
+ // The framework's tenant identity table exists before application migrations.
4
+ // Keep this in parity with TENANT_IDENTITY_SQL in postgres/src/provision.rs.
5
+ const identitySchema = `CREATE TABLE members (
6
+ id text PRIMARY KEY, account_id text NOT NULL UNIQUE, status text NOT NULL DEFAULT 'active',
7
+ display_name text NOT NULL DEFAULT '', avatar_url text NOT NULL DEFAULT '',
8
+ role text NOT NULL DEFAULT 'member', permissions jsonb NOT NULL DEFAULT '{}'::jsonb,
9
+ membership_revision bigint NOT NULL DEFAULT 1,
10
+ created_at timestamptz NOT NULL DEFAULT now(), updated_at timestamptz NOT NULL DEFAULT now()
11
+ )`;
12
+ /** Compile schema using PostgreSQL itself, without connecting to a service. */
13
+ export async function compileLocalSchema(migrations) {
14
+ const db = new PGlite({ extensions: { pg_trgm } });
15
+ try {
16
+ await db.exec(identitySchema);
17
+ for (const migration of migrations) {
18
+ // gen_random_uuid is built into modern PostgreSQL. PGlite does not ship
19
+ // pgcrypto's OpenSSL extension; remaining references fail explicitly.
20
+ const sql = migration.sql.replace(/CREATE\s+EXTENSION\s+(?:IF\s+NOT\s+EXISTS\s+)?["']?pgcrypto["']?\s*;/gi, "");
21
+ try {
22
+ await db.exec(sql);
23
+ }
24
+ catch (error) {
25
+ throw new Error(`Local schema compilation failed in ${migration.name}: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
26
+ }
27
+ }
28
+ const { rows } = await db.query(`SELECT c.relname AS table_name, a.attname AS column_name,
29
+ format_type(a.atttypid, a.atttypmod) AS type, NOT a.attnotnull AS nullable,
30
+ pg_get_expr(d.adbin, d.adrelid) AS column_default,
31
+ EXISTS (SELECT 1 FROM pg_index i WHERE i.indrelid=c.oid AND i.indisprimary AND a.attnum=ANY(i.indkey)) AS primary_key
32
+ FROM pg_class c JOIN pg_namespace n ON c.relnamespace=n.oid
33
+ JOIN pg_attribute a ON a.attrelid=c.oid AND a.attnum>0 AND NOT a.attisdropped
34
+ LEFT JOIN pg_attrdef d ON d.adrelid=c.oid AND d.adnum=a.attnum
35
+ WHERE n.nspname='public' AND c.relkind='r'
36
+ ORDER BY c.relname, a.attnum`);
37
+ const schema = {};
38
+ for (const row of rows) {
39
+ if (row.table_name.startsWith("_gonvex"))
40
+ continue;
41
+ const table = schema[row.table_name] ??= { key: "_id", columns: {} };
42
+ if (row.primary_key)
43
+ table.key = row.column_name;
44
+ table.columns[row.column_name] = {
45
+ type: row.type, nullable: row.nullable,
46
+ ...(row.column_default ? { default: row.column_default } : {}),
47
+ };
48
+ }
49
+ return schema;
50
+ }
51
+ finally {
52
+ await db.close();
53
+ }
54
+ }
55
+ //# sourceMappingURL=local-schema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"local-schema.js","sourceRoot":"","sources":["../src/local-schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAC9C,OAAO,EAAE,OAAO,EAAE,MAAM,sCAAsC,CAAC;AAI/D,8EAA8E;AAC9E,6EAA6E;AAC7E,MAAM,cAAc,GAAG;;;;;;EAMrB,CAAC;AAEH,+EAA+E;AAC/E,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,UAAoD;IAC3F,MAAM,EAAE,GAAG,IAAI,MAAM,CAAC,EAAE,UAAU,EAAE,EAAE,OAAO,EAAE,EAAE,CAAC,CAAC;IACnD,IAAI,CAAC;QACH,MAAM,EAAE,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;QAC9B,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE,CAAC;YACnC,wEAAwE;YACxE,sEAAsE;YACtE,MAAM,GAAG,GAAG,SAAS,CAAC,GAAG,CAAC,OAAO,CAAC,wEAAwE,EAAE,EAAE,CAAC,CAAC;YAChH,IAAI,CAAC;gBAAC,MAAM,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YAAC,CAAC;YAC3B,OAAO,KAAK,EAAE,CAAC;gBAAC,MAAM,IAAI,KAAK,CAAC,sCAAsC,SAAS,CAAC,IAAI,KAAK,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;YAAC,CAAC;QACzK,CAAC;QACD,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,EAAE,CAAC,KAAK,CAG5B;;;;;;;;mCAQ4B,CAAC,CAAC;QACjC,MAAM,MAAM,GAAgB,EAAE,CAAC;QAC/B,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;YACvB,IAAI,GAAG,CAAC,UAAU,CAAC,UAAU,CAAC,SAAS,CAAC;gBAAE,SAAS;YACnD,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,UAAU,CAAC,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC;YACrE,IAAI,GAAG,CAAC,WAAW;gBAAE,KAAK,CAAC,GAAG,GAAG,GAAG,CAAC,WAAW,CAAC;YACjD,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,GAAG;gBAC/B,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,QAAQ,EAAE,GAAG,CAAC,QAAQ;gBACtC,GAAG,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,GAAG,CAAC,cAAc,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aAC/D,CAAC;QACJ,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC;YAAS,CAAC;QAAC,MAAM,EAAE,CAAC,KAAK,EAAE,CAAC;IAAC,CAAC;AACjC,CAAC"}
@@ -75,6 +75,27 @@ export type LiveQueryPlan = {
75
75
  search?: {
76
76
  argument: string;
77
77
  columns: string[];
78
+ booleanTerms?: boolean;
79
+ offlineColumns?: string[];
80
+ sources?: {
81
+ table: string;
82
+ key: string;
83
+ column: string;
84
+ dependencies?: string[];
85
+ }[];
86
+ };
87
+ index?: {
88
+ table: string;
89
+ key: string;
90
+ columns: string[];
91
+ sortColumns?: Record<string, string[]>;
92
+ dependencies?: string[];
93
+ referenceFields?: Record<string, {
94
+ table: string;
95
+ foreignKey: string;
96
+ column: string;
97
+ fallback?: JsonValue;
98
+ }>;
78
99
  };
79
100
  filters?: {
80
101
  argument: string;
@@ -100,13 +121,19 @@ export type LiveQueryPlan = {
100
121
  };
101
122
  export type FilterOperator = "contains" | "notContains" | "equals" | "notEquals" | "startsWith" | "endsWith" | "empty" | "notEmpty" | "oneOf" | "lessThan" | "lessThanOrEqual" | "greaterThan" | "greaterThanOrEqual" | "inRange";
102
123
  export type LiveExpression = {
103
- operator: "eq" | "neq" | "gt" | "gte" | "lt" | "lte" | "in" | "contains" | "containsInsensitive" | "range" | "and" | "or" | "not" | "server";
124
+ operator: "eq" | "neq" | "gt" | "gte" | "lt" | "lte" | "in" | "contains" | "containsInsensitive" | "range" | "and" | "or" | "not" | "server" | "inRelation" | "arrayContains";
104
125
  column?: string;
105
126
  value?: LiveValue;
106
127
  valueTo?: LiveValue;
107
128
  children?: LiveExpression[];
129
+ relation?: {
130
+ table: string;
131
+ column: string;
132
+ where?: LiveExpression;
133
+ };
108
134
  };
109
135
  export type LiveValue = {
136
+ context?: "account.id" | "member.id" | "tenant.id";
110
137
  argument?: string;
111
138
  literal?: unknown;
112
139
  };
@@ -166,7 +166,7 @@ export async function buildModuleArtifact(options) {
166
166
  if (existsSync(clientContractPath)) {
167
167
  const bytes = await readFile(clientContractPath);
168
168
  const contract = JSON.parse(bytes.toString());
169
- if (!Number.isSafeInteger(contract.version) || contract.version < 1 || !Number.isSafeInteger(contract.offlineMaxAgeMs) || contract.offlineMaxAgeMs <= 0)
169
+ if (!Number.isSafeInteger(contract.version) || contract.version < 1 || (contract.offlineMaxAgeMs !== null && (!Number.isSafeInteger(contract.offlineMaxAgeMs) || contract.offlineMaxAgeMs <= 0)))
170
170
  throw new Error("Invalid client-contract.json");
171
171
  files["client-contract.json"] = bytes.toString("base64");
172
172
  }
@@ -1040,7 +1040,11 @@ function normalizeDelivery(value) {
1040
1040
  return undefined;
1041
1041
  }
1042
1042
  function liveQueryPlanFromOptions(options) {
1043
- return parseLiveQueryPlan(options.get("liveQueryPlan")?.value);
1043
+ const declared = options.get("liveQueryPlan");
1044
+ const plan = parseLiveQueryPlan(declared?.value);
1045
+ if (declared && !plan)
1046
+ throw new Error('liveQueryPlan must be a literal object, including nested metadata');
1047
+ return plan;
1044
1048
  }
1045
1049
  function parseLiveQueryPlan(value) {
1046
1050
  const table = stringMember(value, "table");
@@ -1061,9 +1065,62 @@ function parseLiveQueryPlan(value) {
1061
1065
  plan.where = where;
1062
1066
  const searchValue = readMember(value, "search");
1063
1067
  const searchArgument = stringMember(searchValue, "argument");
1064
- const searchColumns = stringArray(readMember(searchValue, "columns"));
1065
- if (searchArgument && searchColumns)
1068
+ const rawSearchColumns = readMember(searchValue, "columns");
1069
+ const searchColumns = Array.isArray(rawSearchColumns) && rawSearchColumns.length === 0 ? [] : stringArray(rawSearchColumns);
1070
+ if (searchArgument && searchColumns) {
1066
1071
  plan.search = { argument: searchArgument, columns: searchColumns };
1072
+ if (readMember(searchValue, 'booleanTerms') === true)
1073
+ plan.search.booleanTerms = true;
1074
+ const offlineColumns = stringArray(readMember(searchValue, 'offlineColumns'));
1075
+ if (offlineColumns)
1076
+ plan.search.offlineColumns = offlineColumns;
1077
+ const sources = readMember(searchValue, 'sources');
1078
+ if (sources !== undefined) {
1079
+ if (!Array.isArray(sources))
1080
+ throw new Error('Search sources must be a literal array');
1081
+ plan.search.sources = sources.map(source => {
1082
+ const table = stringMember(source, 'table'), key = stringMember(source, 'key'), column = stringMember(source, 'column');
1083
+ if (!table || !key || !column)
1084
+ throw new Error('Search sources require table, key and column');
1085
+ const dependencies = stringArray(readMember(source, 'dependencies'));
1086
+ return { table, key, column, ...(dependencies ? { dependencies } : {}) };
1087
+ });
1088
+ }
1089
+ }
1090
+ const indexValue = readMember(value, 'index');
1091
+ if (indexValue !== undefined) {
1092
+ const table = stringMember(indexValue, 'table'), key = stringMember(indexValue, 'key');
1093
+ const columns = stringArray(readMember(indexValue, 'columns'));
1094
+ if (!table || !key || !columns)
1095
+ throw new Error('Live Query indexes require literal table, key and columns');
1096
+ plan.index = { table, key, columns };
1097
+ const referenceFields = readMember(indexValue, 'referenceFields');
1098
+ if (referenceFields !== undefined) {
1099
+ if (!isJsonObject(referenceFields))
1100
+ throw new Error('Index referenceFields must be a literal object');
1101
+ plan.index.referenceFields = Object.fromEntries(Object.entries(referenceFields).map(([name, field]) => {
1102
+ const table = stringMember(field, 'table'), foreignKey = stringMember(field, 'foreignKey'), column = stringMember(field, 'column');
1103
+ if (!table || !foreignKey || !column)
1104
+ throw new Error('Index reference fields require table, foreignKey and column');
1105
+ const fallback = readMember(field, 'fallback');
1106
+ return [name, { table, foreignKey, column, ...(fallback !== undefined ? { fallback } : {}) }];
1107
+ }));
1108
+ }
1109
+ const dependencies = stringArray(readMember(indexValue, 'dependencies'));
1110
+ if (dependencies)
1111
+ plan.index.dependencies = dependencies;
1112
+ const sortColumns = readMember(indexValue, 'sortColumns');
1113
+ if (sortColumns !== undefined) {
1114
+ if (!isJsonObject(sortColumns))
1115
+ throw new Error('Index sortColumns must be a literal object');
1116
+ plan.index.sortColumns = Object.fromEntries(Object.entries(sortColumns).map(([column, value]) => {
1117
+ const fields = stringArray(value);
1118
+ if (!fields?.length)
1119
+ throw new Error('Index sort columns must be a nonempty string array');
1120
+ return [column, fields];
1121
+ }));
1122
+ }
1123
+ }
1067
1124
  const filtersValue = readMember(value, "filters");
1068
1125
  const filtersArgument = stringMember(filtersValue, "argument");
1069
1126
  const filtersColumns = stringArray(readMember(filtersValue, "allowedColumns"));
@@ -1112,10 +1169,20 @@ function parseLiveExpression(value) {
1112
1169
  const operator = stringMember(value, "operator");
1113
1170
  if (!operator || ![
1114
1171
  "eq", "neq", "gt", "gte", "lt", "lte", "in", "contains",
1115
- "containsInsensitive", "range", "and", "or", "not", "server",
1172
+ "containsInsensitive", "range", "and", "or", "not", "server", "inRelation", "arrayContains",
1116
1173
  ].includes(operator))
1117
1174
  return undefined;
1118
1175
  const expression = { operator: operator };
1176
+ if (operator === 'inRelation') {
1177
+ const relation = readMember(value, 'relation');
1178
+ const table = stringMember(relation, 'table'), column = stringMember(relation, 'column');
1179
+ if (!table || !column)
1180
+ throw new Error('Related query scopes require a literal table and column');
1181
+ const where = parseLiveExpression(readMember(relation, 'where'));
1182
+ if (readMember(relation, 'where') !== undefined && !where)
1183
+ throw new Error('Invalid related query predicate');
1184
+ expression.relation = { table, column, ...(where ? { where } : {}) };
1185
+ }
1119
1186
  const column = stringMember(value, "column");
1120
1187
  if (column)
1121
1188
  expression.column = column;
@@ -1134,6 +1201,12 @@ function parseLiveExpression(value) {
1134
1201
  return expression;
1135
1202
  }
1136
1203
  function parseLiveValue(value) {
1204
+ const context = stringMember(value, 'context');
1205
+ if (context) {
1206
+ if (!['account.id', 'member.id', 'tenant.id'].includes(context))
1207
+ throw new Error('Invalid query identity context');
1208
+ return { context: context };
1209
+ }
1137
1210
  const argument = stringMember(value, "argument");
1138
1211
  if (argument)
1139
1212
  return { argument };