@onyx.dev/onyx-database 0.3.0 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -42,6 +42,22 @@ npm i @onyx.dev/onyx-database
42
42
 
43
43
  The package is dual-module (ESM + CJS) and has **no runtime or peer dependencies**.
44
44
 
45
+ To use the bundled CLIs (`onyx-gen` and `onyx-schema`) globally:
46
+
47
+ ```bash
48
+ npm install -g @onyx.dev/onyx-database
49
+ ```
50
+
51
+ To install the CLI globally from this repo checkout (useful for local development and testing):
52
+
53
+ ```bash
54
+ # run from the repo root
55
+ npm install
56
+ npm run build
57
+ npm uninstall -g @onyx.dev/onyx-database # optional: clear older global versions
58
+ npm install -g . # installs the built onyx-schema and onyx-gen
59
+ ```
60
+
45
61
  ---
46
62
 
47
63
  ## Initialize the client
@@ -148,6 +164,9 @@ onyx-schema publish
148
164
  # Overwrite ./onyx.schema.json with the remote schema
149
165
  onyx-schema get
150
166
 
167
+ # Print the remote schema without writing a file
168
+ onyx-schema get --print
169
+
151
170
  # Fetch only selected tables (prints to stdout; does not overwrite files)
152
171
  onyx-schema get --tables=User,Profile
153
172
 
@@ -174,6 +193,10 @@ onyx-schema get --tables=User,Profile
174
193
 
175
194
  # Validate a schema file without publishing
176
195
  onyx-schema validate ./onyx.schema.json
196
+
197
+ # Diff local schema vs API
198
+ onyx-schema diff ./onyx.schema.json
199
+ # Prints YAML with added/removed/changed tables and attribute differences between the API schema and your local file.
177
200
  ```
178
201
 
179
202
  When `--tables` is provided, the subset is printed to stdout instead of writing a
@@ -190,6 +213,16 @@ npm run schema:publish # validate then publish the local schema
190
213
  The CLI reuses the same configuration resolution as `onyx.init()` (env vars,
191
214
  project config, and home profile files).
192
215
 
216
+ Programmatic diffing is also available:
217
+
218
+ ```ts
219
+ import { onyx } from '@onyx.dev/onyx-database';
220
+
221
+ const db = onyx.init();
222
+ const diff = await db.diffSchema(localSchema); // SchemaUpsertRequest
223
+ console.log(diff.changedTables);
224
+ ```
225
+
193
226
  You can also emit to multiple paths in one run (comma-separated or by repeating `--out`):
194
227
 
195
228
  ```bash
@@ -322,7 +355,9 @@ Importable helpers for conditions and sort:
322
355
 
323
356
  ```ts
324
357
  import {
325
- eq, neq, inOp, notIn, between,
358
+ eq, neq, within, notWithin, // preferred aliases for IN/NOT IN
359
+ inOp, notIn,
360
+ between,
326
361
  gt, gte, lt, lte,
327
362
  like, notLike, contains, notContains,
328
363
  startsWith, notStartsWith, matches, notMatches,
@@ -331,6 +366,41 @@ import {
331
366
  } from '@onyx.dev/onyx-database';
332
367
  ```
333
368
 
369
+ - Prefer `within`/`notWithin` for inclusion checks (supports arrays, comma-separated strings, or inner queries).
370
+ - `inOp`/`notIn` remain available for backward compatibility and are exact aliases.
371
+
372
+ ### Inner queries (IN/NOT IN with sub-selects)
373
+
374
+ You can pass another query builder to `within` or `notWithin` to create nested filters. The SDK serializes the inner query (including its table) before sending the request.
375
+
376
+ ```ts
377
+ import { onyx, within, notWithin, eq, tables, Schema } from '@onyx.dev/onyx-database';
378
+
379
+ const db = onyx.init<Schema>();
380
+
381
+ // Users that HAVE the admin role
382
+ const usersWithAdmin = await db
383
+ .from(tables.User)
384
+ .where(
385
+ within(
386
+ 'id',
387
+ db.select('userId').from(tables.UserRole).where(eq('roleId', 'role-admin')),
388
+ ),
389
+ )
390
+ .list();
391
+
392
+ // Roles that DO NOT include a specific permission
393
+ const rolesMissingPermission = await db
394
+ .from(tables.Role)
395
+ .where(
396
+ notWithin(
397
+ 'id',
398
+ db.from(tables.RolePermission).where(eq('permissionId', 'perm-manage-users')),
399
+ ),
400
+ )
401
+ .list();
402
+ ```
403
+
334
404
  ---
335
405
 
336
406
  ## Usage examples with `User`, `Role`, `Permission`
@@ -76,7 +76,7 @@ function readEnv(targetId) {
76
76
  return res;
77
77
  }
78
78
  async function readProjectFile(databaseId) {
79
- if (!isNode) return {};
79
+ if (!isNode) return { config: {} };
80
80
  const fs = await nodeImport("node:fs/promises");
81
81
  const path = await nodeImport("node:path");
82
82
  const cwd = gProcess?.cwd?.() ?? ".";
@@ -85,7 +85,7 @@ async function readProjectFile(databaseId) {
85
85
  const sanitized = txt.replace(/[\r\n]+/g, "");
86
86
  const json = dropUndefined(JSON.parse(sanitized));
87
87
  dbg("project file:", p, "\u2192", mask(json));
88
- return json;
88
+ return { config: json, path: p };
89
89
  };
90
90
  if (databaseId) {
91
91
  const specific = path.resolve(cwd, `onyx-database-${databaseId}.json`);
@@ -100,11 +100,11 @@ async function readProjectFile(databaseId) {
100
100
  return await tryRead(fallback);
101
101
  } catch {
102
102
  dbg("project file not found:", fallback);
103
- return {};
103
+ return { config: {} };
104
104
  }
105
105
  }
106
106
  async function readHomeProfile(databaseId) {
107
- if (!isNode) return {};
107
+ if (!isNode) return { config: {} };
108
108
  const fs = await nodeImport("node:fs/promises");
109
109
  const os = await nodeImport("node:os");
110
110
  const path = await nodeImport("node:path");
@@ -124,7 +124,7 @@ async function readHomeProfile(databaseId) {
124
124
  const sanitized = txt.replace(/[\r\n]+/g, "");
125
125
  const json = dropUndefined(JSON.parse(sanitized));
126
126
  dbg("home profile used:", p, "\u2192", mask(json));
127
- return json;
127
+ return { config: json, path: p };
128
128
  } catch (e) {
129
129
  const msg = e instanceof Error ? e.message : String(e);
130
130
  throw new OnyxConfigError(`Failed to read ${p}: ${msg}`);
@@ -143,7 +143,7 @@ async function readHomeProfile(databaseId) {
143
143
  dbg("no home-root fallback:", defaultInHome);
144
144
  if (!await fileExists(dir)) {
145
145
  dbg("~/.onyx does not exist:", dir);
146
- return {};
146
+ return { config: {} };
147
147
  }
148
148
  const files = await fs.readdir(dir).catch(() => []);
149
149
  const matches = files.filter((f) => f.startsWith("onyx-database-") && f.endsWith(".json"));
@@ -157,10 +157,10 @@ async function readHomeProfile(databaseId) {
157
157
  );
158
158
  }
159
159
  dbg("no usable home profiles found in", dir);
160
- return {};
160
+ return { config: {} };
161
161
  }
162
162
  async function readConfigPath(p) {
163
- if (!isNode) return {};
163
+ if (!isNode) return { config: {} };
164
164
  const fs = await nodeImport("node:fs/promises");
165
165
  const path = await nodeImport("node:path");
166
166
  const cwd = gProcess?.cwd?.() ?? ".";
@@ -170,7 +170,7 @@ async function readConfigPath(p) {
170
170
  const sanitized = txt.replace(/[\r\n]+/g, "");
171
171
  const json = dropUndefined(JSON.parse(sanitized));
172
172
  dbg("config path:", resolved, "\u2192", mask(json));
173
- return json;
173
+ return { config: json, path: resolved };
174
174
  } catch (e) {
175
175
  const msg = e instanceof Error ? e.message : String(e);
176
176
  throw new OnyxConfigError(`Failed to read ${resolved}: ${msg}`);
@@ -181,7 +181,8 @@ async function resolveConfig(input) {
181
181
  const env = readEnv(input?.databaseId);
182
182
  let cfgPath = {};
183
183
  if (configPath) {
184
- cfgPath = await readConfigPath(configPath);
184
+ const cfgRes = await readConfigPath(configPath);
185
+ cfgPath = cfgRes.config;
185
186
  }
186
187
  const targetId = input?.databaseId ?? env.databaseId ?? cfgPath.databaseId;
187
188
  let haveDbId = !!(input?.databaseId ?? env.databaseId ?? cfgPath.databaseId);
@@ -189,14 +190,16 @@ async function resolveConfig(input) {
189
190
  let haveApiSecret = !!(input?.apiSecret ?? env.apiSecret ?? cfgPath.apiSecret);
190
191
  let project = {};
191
192
  if (!(haveDbId && haveApiKey && haveApiSecret)) {
192
- project = await readProjectFile(targetId);
193
+ const projRes = await readProjectFile(targetId);
194
+ project = projRes.config;
193
195
  if (project.databaseId) haveDbId = true;
194
196
  if (project.apiKey) haveApiKey = true;
195
197
  if (project.apiSecret) haveApiSecret = true;
196
198
  }
197
199
  let home = {};
198
200
  if (!(haveDbId && haveApiKey && haveApiSecret)) {
199
- home = await readHomeProfile(targetId);
201
+ const homeRes = await readHomeProfile(targetId);
202
+ home = homeRes.config;
200
203
  }
201
204
  const merged = {
202
205
  baseUrl: DEFAULT_BASE_URL,
@@ -931,6 +934,56 @@ var QueryResults = class extends Array {
931
934
  }
932
935
  };
933
936
 
937
+ // src/helpers/condition-normalizer.ts
938
+ function isQueryBuilderLike(value) {
939
+ return !!value && typeof value.toSerializableQueryObject === "function";
940
+ }
941
+ function normalizeCriteriaValue(value) {
942
+ if (Array.isArray(value)) {
943
+ let changed = false;
944
+ const normalized = value.map((item) => {
945
+ const result = normalizeCriteriaValue(item);
946
+ if (result.changed) changed = true;
947
+ return result.value;
948
+ });
949
+ if (!changed) {
950
+ for (let i = 0; i < normalized.length; i += 1) {
951
+ if (normalized[i] !== value[i]) {
952
+ changed = true;
953
+ break;
954
+ }
955
+ }
956
+ }
957
+ return { value: changed ? normalized : value, changed };
958
+ }
959
+ if (isQueryBuilderLike(value)) {
960
+ return { value: value.toSerializableQueryObject(), changed: true };
961
+ }
962
+ return { value, changed: false };
963
+ }
964
+ function normalizeConditionInternal(condition) {
965
+ if (condition.conditionType === "SingleCondition") {
966
+ const { value, changed: changed2 } = normalizeCriteriaValue(condition.criteria.value);
967
+ if (!changed2) return condition;
968
+ return {
969
+ ...condition,
970
+ criteria: { ...condition.criteria, value }
971
+ };
972
+ }
973
+ let changed = false;
974
+ const normalizedConditions = condition.conditions.map((child) => {
975
+ const normalized = normalizeConditionInternal(child);
976
+ if (normalized !== child) changed = true;
977
+ return normalized;
978
+ });
979
+ if (!changed) return condition;
980
+ return { ...condition, conditions: normalizedConditions };
981
+ }
982
+ function normalizeCondition(condition) {
983
+ if (!condition) return condition;
984
+ return normalizeConditionInternal(condition);
985
+ }
986
+
934
987
  // src/builders/cascade-relationship-builder.ts
935
988
  var CascadeRelationshipBuilder = class {
936
989
  graphName;
@@ -1005,6 +1058,194 @@ var OnyxError = class extends Error {
1005
1058
  }
1006
1059
  };
1007
1060
 
1061
+ // src/helpers/schema-diff.ts
1062
+ function mapByName(items) {
1063
+ const map = /* @__PURE__ */ new Map();
1064
+ for (const item of items ?? []) {
1065
+ if (!item?.name) continue;
1066
+ map.set(item.name, item);
1067
+ }
1068
+ return map;
1069
+ }
1070
+ function normalizeEntities(schema) {
1071
+ if (Array.isArray(schema.entities)) {
1072
+ return schema.entities ?? [];
1073
+ }
1074
+ const tables = schema.tables;
1075
+ if (!Array.isArray(tables)) return [];
1076
+ return tables.map((table) => ({
1077
+ name: table.name,
1078
+ attributes: table.attributes ?? []
1079
+ }));
1080
+ }
1081
+ function normalizePartition(partition) {
1082
+ if (partition == null) return "";
1083
+ const trimmed = partition.trim();
1084
+ return trimmed;
1085
+ }
1086
+ function identifiersEqual(a, b) {
1087
+ if (!a && !b) return true;
1088
+ if (!a || !b) return false;
1089
+ return a.name === b.name && a.generator === b.generator && a.type === b.type;
1090
+ }
1091
+ function diffAttributes(apiAttrs, localAttrs) {
1092
+ const apiMap = mapByName(apiAttrs);
1093
+ const localMap = mapByName(localAttrs);
1094
+ const added = [];
1095
+ const removed = [];
1096
+ const changed = [];
1097
+ for (const [name, local] of localMap.entries()) {
1098
+ if (!apiMap.has(name)) {
1099
+ added.push(local);
1100
+ continue;
1101
+ }
1102
+ const api = apiMap.get(name);
1103
+ const apiNull = Boolean(api.isNullable);
1104
+ const localNull = Boolean(local.isNullable);
1105
+ if (api.type !== local.type || apiNull !== localNull) {
1106
+ changed.push({
1107
+ name,
1108
+ from: { type: api.type, isNullable: apiNull },
1109
+ to: { type: local.type, isNullable: localNull }
1110
+ });
1111
+ }
1112
+ }
1113
+ for (const name of apiMap.keys()) {
1114
+ if (!localMap.has(name)) removed.push(name);
1115
+ }
1116
+ added.sort((a, b) => a.name.localeCompare(b.name));
1117
+ removed.sort();
1118
+ changed.sort((a, b) => a.name.localeCompare(b.name));
1119
+ if (!added.length && !removed.length && !changed.length) return null;
1120
+ return { added, removed, changed };
1121
+ }
1122
+ function diffIndexes(apiIndexes, localIndexes) {
1123
+ const apiMap = mapByName(apiIndexes);
1124
+ const localMap = mapByName(localIndexes);
1125
+ const added = [];
1126
+ const removed = [];
1127
+ const changed = [];
1128
+ for (const [name, local] of localMap.entries()) {
1129
+ if (!apiMap.has(name)) {
1130
+ added.push(local);
1131
+ continue;
1132
+ }
1133
+ const api = apiMap.get(name);
1134
+ const apiType = api.type ?? "DEFAULT";
1135
+ const localType = local.type ?? "DEFAULT";
1136
+ const apiScore = api.minimumScore;
1137
+ const localScore = local.minimumScore;
1138
+ if (apiType !== localType || apiScore !== localScore) {
1139
+ changed.push({ name, from: api, to: local });
1140
+ }
1141
+ }
1142
+ for (const name of apiMap.keys()) {
1143
+ if (!localMap.has(name)) removed.push(name);
1144
+ }
1145
+ added.sort((a, b) => a.name.localeCompare(b.name));
1146
+ removed.sort();
1147
+ changed.sort((a, b) => a.name.localeCompare(b.name));
1148
+ if (!added.length && !removed.length && !changed.length) return null;
1149
+ return { added, removed, changed };
1150
+ }
1151
+ function diffResolvers(apiResolvers, localResolvers) {
1152
+ const apiMap = mapByName(apiResolvers);
1153
+ const localMap = mapByName(localResolvers);
1154
+ const added = [];
1155
+ const removed = [];
1156
+ const changed = [];
1157
+ for (const [name, local] of localMap.entries()) {
1158
+ if (!apiMap.has(name)) {
1159
+ added.push(local);
1160
+ continue;
1161
+ }
1162
+ const api = apiMap.get(name);
1163
+ if (api.resolver !== local.resolver) {
1164
+ changed.push({ name, from: api, to: local });
1165
+ }
1166
+ }
1167
+ for (const name of apiMap.keys()) {
1168
+ if (!localMap.has(name)) removed.push(name);
1169
+ }
1170
+ added.sort((a, b) => a.name.localeCompare(b.name));
1171
+ removed.sort();
1172
+ changed.sort((a, b) => a.name.localeCompare(b.name));
1173
+ if (!added.length && !removed.length && !changed.length) return null;
1174
+ return { added, removed, changed };
1175
+ }
1176
+ function diffTriggers(apiTriggers, localTriggers) {
1177
+ const apiMap = mapByName(apiTriggers);
1178
+ const localMap = mapByName(localTriggers);
1179
+ const added = [];
1180
+ const removed = [];
1181
+ const changed = [];
1182
+ for (const [name, local] of localMap.entries()) {
1183
+ if (!apiMap.has(name)) {
1184
+ added.push(local);
1185
+ continue;
1186
+ }
1187
+ const api = apiMap.get(name);
1188
+ if (api.event !== local.event || api.trigger !== local.trigger) {
1189
+ changed.push({ name, from: api, to: local });
1190
+ }
1191
+ }
1192
+ for (const name of apiMap.keys()) {
1193
+ if (!localMap.has(name)) removed.push(name);
1194
+ }
1195
+ added.sort((a, b) => a.name.localeCompare(b.name));
1196
+ removed.sort();
1197
+ changed.sort((a, b) => a.name.localeCompare(b.name));
1198
+ if (!added.length && !removed.length && !changed.length) return null;
1199
+ return { added, removed, changed };
1200
+ }
1201
+ function computeSchemaDiff(apiSchema, localSchema) {
1202
+ const apiEntities = normalizeEntities(apiSchema);
1203
+ const localEntities = normalizeEntities(localSchema);
1204
+ const apiMap = mapByName(apiEntities);
1205
+ const localMap = mapByName(localEntities);
1206
+ const newTables = [];
1207
+ const removedTables = [];
1208
+ const changedTables = [];
1209
+ for (const [name, localEntity] of localMap.entries()) {
1210
+ if (!apiMap.has(name)) {
1211
+ newTables.push(name);
1212
+ continue;
1213
+ }
1214
+ const apiEntity = apiMap.get(name);
1215
+ const tableDiff = { name };
1216
+ const partitionFrom = normalizePartition(apiEntity.partition);
1217
+ const partitionTo = normalizePartition(localEntity.partition);
1218
+ if (partitionFrom !== partitionTo) {
1219
+ tableDiff.partition = { from: partitionFrom || null, to: partitionTo || null };
1220
+ }
1221
+ if (!identifiersEqual(apiEntity.identifier, localEntity.identifier)) {
1222
+ tableDiff.identifier = {
1223
+ from: apiEntity.identifier ?? null,
1224
+ to: localEntity.identifier ?? null
1225
+ };
1226
+ }
1227
+ const attrs = diffAttributes(apiEntity.attributes, localEntity.attributes);
1228
+ if (attrs) tableDiff.attributes = attrs;
1229
+ const indexes = diffIndexes(apiEntity.indexes, localEntity.indexes);
1230
+ if (indexes) tableDiff.indexes = indexes;
1231
+ const resolvers = diffResolvers(apiEntity.resolvers, localEntity.resolvers);
1232
+ if (resolvers) tableDiff.resolvers = resolvers;
1233
+ const triggers = diffTriggers(apiEntity.triggers, localEntity.triggers);
1234
+ if (triggers) tableDiff.triggers = triggers;
1235
+ const hasChange = tableDiff.partition || tableDiff.identifier || tableDiff.attributes || tableDiff.indexes || tableDiff.resolvers || tableDiff.triggers;
1236
+ if (hasChange) {
1237
+ changedTables.push(tableDiff);
1238
+ }
1239
+ }
1240
+ for (const name of apiMap.keys()) {
1241
+ if (!localMap.has(name)) removedTables.push(name);
1242
+ }
1243
+ newTables.sort();
1244
+ removedTables.sort();
1245
+ changedTables.sort((a, b) => a.name.localeCompare(b.name));
1246
+ return { newTables, removedTables, changedTables };
1247
+ }
1248
+
1008
1249
  // src/impl/onyx.ts
1009
1250
  var DEFAULT_CACHE_TTL = 5 * 60 * 1e3;
1010
1251
  var cachedCfg = null;
@@ -1058,6 +1299,10 @@ function serializeDates(value) {
1058
1299
  }
1059
1300
  return value;
1060
1301
  }
1302
+ function stripEntityText(input) {
1303
+ const { entityText, ...rest } = input;
1304
+ return rest;
1305
+ }
1061
1306
  function normalizeSecretMetadata(input) {
1062
1307
  return { ...input, updatedAt: new Date(input.updatedAt) };
1063
1308
  }
@@ -1071,7 +1316,20 @@ function normalizeDate(value) {
1071
1316
  return Number.isNaN(ts.getTime()) ? void 0 : ts;
1072
1317
  }
1073
1318
  function normalizeSchemaRevision(input, fallbackDatabaseId) {
1074
- const { meta, createdAt, publishedAt, revisionId, ...rest } = input;
1319
+ const {
1320
+ meta,
1321
+ createdAt,
1322
+ publishedAt,
1323
+ revisionId,
1324
+ entityText,
1325
+ databaseId,
1326
+ entities,
1327
+ revisionDescription,
1328
+ ...rest
1329
+ } = input;
1330
+ const dbId = typeof databaseId === "string" ? databaseId : fallbackDatabaseId;
1331
+ const entityList = Array.isArray(entities) ? entities : [];
1332
+ const revisionDesc = typeof revisionDescription === "string" ? revisionDescription : void 0;
1075
1333
  const mergedMeta = {
1076
1334
  revisionId: meta?.revisionId ?? revisionId,
1077
1335
  createdAt: normalizeDate(meta?.createdAt ?? createdAt),
@@ -1079,10 +1337,11 @@ function normalizeSchemaRevision(input, fallbackDatabaseId) {
1079
1337
  };
1080
1338
  const cleanedMeta = mergedMeta.revisionId || mergedMeta.createdAt || mergedMeta.publishedAt ? mergedMeta : void 0;
1081
1339
  return {
1082
- ...rest,
1083
- databaseId: input.databaseId ?? fallbackDatabaseId,
1340
+ databaseId: dbId,
1341
+ revisionDescription: revisionDesc,
1342
+ entities: entityList,
1084
1343
  meta: cleanedMeta,
1085
- entities: input.entities ?? []
1344
+ ...rest
1086
1345
  };
1087
1346
  }
1088
1347
  var OnyxDatabaseImpl = class {
@@ -1194,7 +1453,8 @@ var OnyxDatabaseImpl = class {
1194
1453
  const path = `/data/${encodeURIComponent(databaseId)}/${encodeURIComponent(
1195
1454
  table
1196
1455
  )}/${encodeURIComponent(primaryKey)}${params.toString() ? `?${params.toString()}` : ""}`;
1197
- return http.request("DELETE", path);
1456
+ await http.request("DELETE", path);
1457
+ return true;
1198
1458
  }
1199
1459
  async saveDocument(doc) {
1200
1460
  const { http, databaseId } = await this.ensureClient();
@@ -1237,20 +1497,32 @@ var OnyxDatabaseImpl = class {
1237
1497
  const res = await http.request("GET", path);
1238
1498
  return Array.isArray(res) ? res.map((entry) => normalizeSchemaRevision(entry, databaseId)) : [];
1239
1499
  }
1500
+ async diffSchema(localSchema) {
1501
+ const apiSchema = await this.getSchema();
1502
+ return computeSchemaDiff(apiSchema, localSchema);
1503
+ }
1240
1504
  async updateSchema(schema, options) {
1241
1505
  const { http, databaseId } = await this.ensureClient();
1242
1506
  const params = new URLSearchParams();
1243
1507
  if (options?.publish) params.append("publish", "true");
1244
1508
  const path = `/schemas/${encodeURIComponent(databaseId)}${params.size ? `?${params.toString()}` : ""}`;
1245
- const body = { ...schema, databaseId: schema.databaseId ?? databaseId };
1246
- const res = await http.request("PUT", path, serializeDates(body));
1509
+ const body = stripEntityText({ ...schema, databaseId: schema.databaseId ?? databaseId });
1510
+ const res = await http.request(
1511
+ "PUT",
1512
+ path,
1513
+ serializeDates(body)
1514
+ );
1247
1515
  return normalizeSchemaRevision(res, databaseId);
1248
1516
  }
1249
1517
  async validateSchema(schema) {
1250
1518
  const { http, databaseId } = await this.ensureClient();
1251
1519
  const path = `/schemas/${encodeURIComponent(databaseId)}/validate`;
1252
- const body = { ...schema, databaseId: schema.databaseId ?? databaseId };
1253
- const res = await http.request("POST", path, serializeDates(body));
1520
+ const body = stripEntityText({ ...schema, databaseId: schema.databaseId ?? databaseId });
1521
+ const res = await http.request(
1522
+ "POST",
1523
+ path,
1524
+ serializeDates(body)
1525
+ );
1254
1526
  const normalizedSchema = res.schema ? normalizeSchemaRevision(res.schema, databaseId) : void 0;
1255
1527
  return {
1256
1528
  ...res,
@@ -1412,11 +1684,14 @@ var QueryBuilderImpl = class {
1412
1684
  if (!this.table) throw new Error("Table is not defined. Call from(<table>) first.");
1413
1685
  return this.table;
1414
1686
  }
1687
+ serializableConditions() {
1688
+ return normalizeCondition(this.conditions);
1689
+ }
1415
1690
  toSelectQuery() {
1416
1691
  return {
1417
1692
  type: "SelectQuery",
1418
1693
  fields: this.fields,
1419
- conditions: this.conditions,
1694
+ conditions: this.serializableConditions(),
1420
1695
  sort: this.sort,
1421
1696
  limit: this.limitValue,
1422
1697
  distinct: this.distinctValue,
@@ -1425,6 +1700,21 @@ var QueryBuilderImpl = class {
1425
1700
  resolvers: this.resolvers
1426
1701
  };
1427
1702
  }
1703
+ toUpdateQuery() {
1704
+ return {
1705
+ type: "UpdateQuery",
1706
+ conditions: this.serializableConditions(),
1707
+ updates: this.updates ?? {},
1708
+ sort: this.sort,
1709
+ limit: this.limitValue,
1710
+ partition: this.partitionValue ?? null
1711
+ };
1712
+ }
1713
+ toSerializableQueryObject() {
1714
+ const table = this.ensureTable();
1715
+ const payload = this.mode === "update" ? this.toUpdateQuery() : this.toSelectQuery();
1716
+ return { ...payload, table };
1717
+ }
1428
1718
  from(table) {
1429
1719
  this.table = table;
1430
1720
  return this;
@@ -1560,14 +1850,7 @@ var QueryBuilderImpl = class {
1560
1850
  async update() {
1561
1851
  if (this.mode !== "update") throw new Error("Call setUpdates(...) before update().");
1562
1852
  const table = this.ensureTable();
1563
- const update = {
1564
- type: "UpdateQuery",
1565
- conditions: this.conditions,
1566
- updates: this.updates ?? {},
1567
- sort: this.sort,
1568
- limit: this.limitValue,
1569
- partition: this.partitionValue ?? null
1570
- };
1853
+ const update = this.toUpdateQuery();
1571
1854
  return this.db._update(table, update, this.partitionValue);
1572
1855
  }
1573
1856
  onItemAdded(listener) {