@onyx.dev/onyx-database 1.0.0 → 1.0.3
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 +134 -2
- package/dist/aggregates-BGXzij4U.d.cts +1424 -0
- package/dist/aggregates-BGXzij4U.d.ts +1424 -0
- package/dist/edge.cjs +1954 -0
- package/dist/edge.cjs.map +1 -0
- package/dist/edge.d.cts +9 -0
- package/dist/edge.d.ts +9 -0
- package/dist/edge.js +1911 -0
- package/dist/edge.js.map +1 -0
- package/dist/gen/cli/generate.cjs +303 -56
- package/dist/gen/cli/generate.cjs.map +1 -1
- package/dist/index.cjs +303 -55
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -1325
- package/dist/index.d.ts +3 -1325
- package/dist/index.js +303 -55
- package/dist/index.js.map +1 -1
- package/dist/schema/cli/schema.cjs +473 -267
- package/dist/schema/cli/schema.cjs.map +1 -1
- package/package.json +22 -5
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
201
|
+
const homeRes = await readHomeProfile(targetId);
|
|
202
|
+
home = homeRes.config;
|
|
200
203
|
}
|
|
201
204
|
const merged = {
|
|
202
205
|
baseUrl: DEFAULT_BASE_URL,
|
|
@@ -215,6 +218,10 @@ async function resolveConfig(input) {
|
|
|
215
218
|
const fetchImpl = merged.fetch ?? (typeof gfetch === "function" ? (u, i) => gfetch(u, i) : async () => {
|
|
216
219
|
throw new OnyxConfigError("No fetch available; provide OnyxConfig.fetch");
|
|
217
220
|
});
|
|
221
|
+
const retryConfig = input?.retry ?? env.retry ?? cfgPath.retry ?? project.retry ?? home.retry ?? {};
|
|
222
|
+
const retryEnabled = retryConfig.enabled ?? true;
|
|
223
|
+
const maxRetries = retryConfig.maxRetries ?? 3;
|
|
224
|
+
const retryInitialDelayMs = retryConfig.initialDelayMs ?? 300;
|
|
218
225
|
const missing = [];
|
|
219
226
|
if (!databaseId) missing.push("databaseId");
|
|
220
227
|
if (!apiKey) missing.push("apiKey");
|
|
@@ -237,7 +244,16 @@ async function resolveConfig(input) {
|
|
|
237
244
|
`Missing required config: ${missing.join(", ")}. Sources: ${sources.join(", ")}`
|
|
238
245
|
);
|
|
239
246
|
}
|
|
240
|
-
const resolved = {
|
|
247
|
+
const resolved = {
|
|
248
|
+
baseUrl,
|
|
249
|
+
databaseId,
|
|
250
|
+
apiKey,
|
|
251
|
+
apiSecret,
|
|
252
|
+
fetch: fetchImpl,
|
|
253
|
+
retryEnabled,
|
|
254
|
+
maxRetries,
|
|
255
|
+
retryInitialDelayMs
|
|
256
|
+
};
|
|
241
257
|
const source = {
|
|
242
258
|
databaseId: input?.databaseId ? "explicit config" : env.databaseId ? "env" : cfgPath.databaseId ? "env ONYX_CONFIG_PATH" : project.databaseId ? "project file" : home.databaseId ? "home profile" : "unknown",
|
|
243
259
|
apiKey: input?.apiKey ? "explicit config" : env.apiKey ? "env" : cfgPath.apiKey ? "env ONYX_CONFIG_PATH" : project.apiKey ? "project file" : home.apiKey ? "home profile" : "unknown",
|
|
@@ -310,7 +326,6 @@ function emitTypes(schema, options) {
|
|
|
310
326
|
};
|
|
311
327
|
const lines = [];
|
|
312
328
|
lines.push(`// AUTO-GENERATED BY onyx-gen. DO NOT EDIT.`);
|
|
313
|
-
lines.push(`// Generated at ${(/* @__PURE__ */ new Date()).toISOString()}`);
|
|
314
329
|
lines.push("");
|
|
315
330
|
for (const t of schema.tables) {
|
|
316
331
|
const typeName = `${opts.modelNamePrefix}${toPascalCase(t.name)}`;
|
|
@@ -385,7 +400,7 @@ function parseJsonAllowNaN(txt) {
|
|
|
385
400
|
return JSON.parse(fixed);
|
|
386
401
|
}
|
|
387
402
|
}
|
|
388
|
-
var HttpClient = class {
|
|
403
|
+
var HttpClient = class _HttpClient {
|
|
389
404
|
baseUrl;
|
|
390
405
|
apiKey;
|
|
391
406
|
apiSecret;
|
|
@@ -393,6 +408,25 @@ var HttpClient = class {
|
|
|
393
408
|
defaults;
|
|
394
409
|
requestLoggingEnabled;
|
|
395
410
|
responseLoggingEnabled;
|
|
411
|
+
retryEnabled;
|
|
412
|
+
maxRetries;
|
|
413
|
+
retryInitialDelayMs;
|
|
414
|
+
shouldRetry;
|
|
415
|
+
static parseRetryAfter(header) {
|
|
416
|
+
if (!header) return null;
|
|
417
|
+
const trimmed = header.trim();
|
|
418
|
+
if (trimmed === "") return null;
|
|
419
|
+
const seconds = Number(trimmed);
|
|
420
|
+
if (Number.isFinite(seconds)) {
|
|
421
|
+
return Math.max(0, seconds * 1e3);
|
|
422
|
+
}
|
|
423
|
+
const dateMs = Date.parse(trimmed);
|
|
424
|
+
if (!Number.isNaN(dateMs)) {
|
|
425
|
+
const now = Date.now();
|
|
426
|
+
return Math.max(0, dateMs - now);
|
|
427
|
+
}
|
|
428
|
+
return null;
|
|
429
|
+
}
|
|
396
430
|
constructor(opts) {
|
|
397
431
|
if (!opts.baseUrl || opts.baseUrl.trim() === "") {
|
|
398
432
|
throw new OnyxConfigError("baseUrl is required");
|
|
@@ -417,6 +451,10 @@ var HttpClient = class {
|
|
|
417
451
|
const envDebug = globalThis.process?.env?.ONYX_DEBUG === "true";
|
|
418
452
|
this.requestLoggingEnabled = !!opts.requestLoggingEnabled || envDebug;
|
|
419
453
|
this.responseLoggingEnabled = !!opts.responseLoggingEnabled || envDebug;
|
|
454
|
+
this.retryEnabled = opts.retryEnabled ?? true;
|
|
455
|
+
this.maxRetries = Math.max(0, opts.maxRetries ?? 2);
|
|
456
|
+
this.retryInitialDelayMs = Math.max(0, opts.retryInitialDelayMs ?? 100);
|
|
457
|
+
this.shouldRetry = (method, path) => method === "GET" || path.startsWith("/query/");
|
|
420
458
|
}
|
|
421
459
|
headers(extra) {
|
|
422
460
|
const extras = { ...extra ?? {} };
|
|
@@ -457,9 +495,8 @@ var HttpClient = class {
|
|
|
457
495
|
headers,
|
|
458
496
|
body: payload
|
|
459
497
|
};
|
|
460
|
-
const
|
|
461
|
-
const
|
|
462
|
-
const maxAttempts = canRetry ? 3 : 1;
|
|
498
|
+
const canRetry = this.retryEnabled && this.shouldRetry(method, path);
|
|
499
|
+
const maxAttempts = canRetry ? this.maxRetries + 1 : 1;
|
|
463
500
|
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
464
501
|
try {
|
|
465
502
|
const res = await this.fetchImpl(url, init);
|
|
@@ -477,7 +514,10 @@ var HttpClient = class {
|
|
|
477
514
|
if (!res.ok) {
|
|
478
515
|
const msg = typeof data === "object" && data !== null && "error" in data && typeof data.error?.message === "string" ? String(data.error.message) : `${res.status} ${res.statusText}`;
|
|
479
516
|
if (canRetry && res.status >= 500 && attempt + 1 < maxAttempts) {
|
|
480
|
-
|
|
517
|
+
const serverRetry = _HttpClient.parseRetryAfter(res.headers.get("retry-after"));
|
|
518
|
+
const backoff = this.retryInitialDelayMs * 2 ** attempt;
|
|
519
|
+
const delay = serverRetry ?? backoff;
|
|
520
|
+
await new Promise((r) => setTimeout(r, delay));
|
|
481
521
|
continue;
|
|
482
522
|
}
|
|
483
523
|
throw new OnyxHttpError(msg, res.status, res.statusText, data, raw);
|
|
@@ -486,7 +526,8 @@ var HttpClient = class {
|
|
|
486
526
|
} catch (err) {
|
|
487
527
|
const retryable = canRetry && (!(err instanceof OnyxHttpError) || err.status >= 500);
|
|
488
528
|
if (attempt + 1 < maxAttempts && retryable) {
|
|
489
|
-
|
|
529
|
+
const delay = this.retryInitialDelayMs * 2 ** attempt;
|
|
530
|
+
await new Promise((r) => setTimeout(r, delay));
|
|
490
531
|
continue;
|
|
491
532
|
}
|
|
492
533
|
throw err;
|
|
@@ -1047,31 +1088,196 @@ var CascadeRelationshipBuilder = class {
|
|
|
1047
1088
|
}
|
|
1048
1089
|
};
|
|
1049
1090
|
|
|
1050
|
-
// src/
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1091
|
+
// src/helpers/schema-diff.ts
|
|
1092
|
+
function mapByName(items) {
|
|
1093
|
+
const map = /* @__PURE__ */ new Map();
|
|
1094
|
+
for (const item of items ?? []) {
|
|
1095
|
+
if (!item?.name) continue;
|
|
1096
|
+
map.set(item.name, item);
|
|
1097
|
+
}
|
|
1098
|
+
return map;
|
|
1099
|
+
}
|
|
1100
|
+
function normalizeEntities(schema) {
|
|
1101
|
+
if (Array.isArray(schema.entities)) {
|
|
1102
|
+
return schema.entities ?? [];
|
|
1103
|
+
}
|
|
1104
|
+
const tables = schema.tables;
|
|
1105
|
+
if (!Array.isArray(tables)) return [];
|
|
1106
|
+
return tables.map((table) => ({
|
|
1107
|
+
name: table.name,
|
|
1108
|
+
attributes: table.attributes ?? []
|
|
1109
|
+
}));
|
|
1110
|
+
}
|
|
1111
|
+
function normalizePartition(partition) {
|
|
1112
|
+
if (partition == null) return "";
|
|
1113
|
+
const trimmed = partition.trim();
|
|
1114
|
+
return trimmed;
|
|
1115
|
+
}
|
|
1116
|
+
function identifiersEqual(a, b) {
|
|
1117
|
+
if (!a && !b) return true;
|
|
1118
|
+
if (!a || !b) return false;
|
|
1119
|
+
return a.name === b.name && a.generator === b.generator && a.type === b.type;
|
|
1120
|
+
}
|
|
1121
|
+
function diffAttributes(apiAttrs, localAttrs) {
|
|
1122
|
+
const apiMap = mapByName(apiAttrs);
|
|
1123
|
+
const localMap = mapByName(localAttrs);
|
|
1124
|
+
const added = [];
|
|
1125
|
+
const removed = [];
|
|
1126
|
+
const changed = [];
|
|
1127
|
+
for (const [name, local] of localMap.entries()) {
|
|
1128
|
+
if (!apiMap.has(name)) {
|
|
1129
|
+
added.push(local);
|
|
1130
|
+
continue;
|
|
1131
|
+
}
|
|
1132
|
+
const api = apiMap.get(name);
|
|
1133
|
+
const apiNull = Boolean(api.isNullable);
|
|
1134
|
+
const localNull = Boolean(local.isNullable);
|
|
1135
|
+
if (api.type !== local.type || apiNull !== localNull) {
|
|
1136
|
+
changed.push({
|
|
1137
|
+
name,
|
|
1138
|
+
from: { type: api.type, isNullable: apiNull },
|
|
1139
|
+
to: { type: local.type, isNullable: localNull }
|
|
1140
|
+
});
|
|
1141
|
+
}
|
|
1055
1142
|
}
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
if (cachedCfg && cachedCfg.expires > now) {
|
|
1065
|
-
return cachedCfg.promise;
|
|
1066
|
-
}
|
|
1067
|
-
const { ttl: _ttl, requestLoggingEnabled: _reqLog, responseLoggingEnabled: _resLog, ...rest } = config ?? {};
|
|
1068
|
-
const promise = resolveConfig(rest);
|
|
1069
|
-
cachedCfg = { promise, expires: now + ttl };
|
|
1070
|
-
return promise;
|
|
1143
|
+
for (const name of apiMap.keys()) {
|
|
1144
|
+
if (!localMap.has(name)) removed.push(name);
|
|
1145
|
+
}
|
|
1146
|
+
added.sort((a, b) => a.name.localeCompare(b.name));
|
|
1147
|
+
removed.sort();
|
|
1148
|
+
changed.sort((a, b) => a.name.localeCompare(b.name));
|
|
1149
|
+
if (!added.length && !removed.length && !changed.length) return null;
|
|
1150
|
+
return { added, removed, changed };
|
|
1071
1151
|
}
|
|
1072
|
-
function
|
|
1073
|
-
|
|
1152
|
+
function diffIndexes(apiIndexes, localIndexes) {
|
|
1153
|
+
const apiMap = mapByName(apiIndexes);
|
|
1154
|
+
const localMap = mapByName(localIndexes);
|
|
1155
|
+
const added = [];
|
|
1156
|
+
const removed = [];
|
|
1157
|
+
const changed = [];
|
|
1158
|
+
for (const [name, local] of localMap.entries()) {
|
|
1159
|
+
if (!apiMap.has(name)) {
|
|
1160
|
+
added.push(local);
|
|
1161
|
+
continue;
|
|
1162
|
+
}
|
|
1163
|
+
const api = apiMap.get(name);
|
|
1164
|
+
const apiType = api.type ?? "DEFAULT";
|
|
1165
|
+
const localType = local.type ?? "DEFAULT";
|
|
1166
|
+
const apiScore = api.minimumScore;
|
|
1167
|
+
const localScore = local.minimumScore;
|
|
1168
|
+
if (apiType !== localType || apiScore !== localScore) {
|
|
1169
|
+
changed.push({ name, from: api, to: local });
|
|
1170
|
+
}
|
|
1171
|
+
}
|
|
1172
|
+
for (const name of apiMap.keys()) {
|
|
1173
|
+
if (!localMap.has(name)) removed.push(name);
|
|
1174
|
+
}
|
|
1175
|
+
added.sort((a, b) => a.name.localeCompare(b.name));
|
|
1176
|
+
removed.sort();
|
|
1177
|
+
changed.sort((a, b) => a.name.localeCompare(b.name));
|
|
1178
|
+
if (!added.length && !removed.length && !changed.length) return null;
|
|
1179
|
+
return { added, removed, changed };
|
|
1180
|
+
}
|
|
1181
|
+
function diffResolvers(apiResolvers, localResolvers) {
|
|
1182
|
+
const apiMap = mapByName(apiResolvers);
|
|
1183
|
+
const localMap = mapByName(localResolvers);
|
|
1184
|
+
const added = [];
|
|
1185
|
+
const removed = [];
|
|
1186
|
+
const changed = [];
|
|
1187
|
+
for (const [name, local] of localMap.entries()) {
|
|
1188
|
+
if (!apiMap.has(name)) {
|
|
1189
|
+
added.push(local);
|
|
1190
|
+
continue;
|
|
1191
|
+
}
|
|
1192
|
+
const api = apiMap.get(name);
|
|
1193
|
+
if (api.resolver !== local.resolver) {
|
|
1194
|
+
changed.push({ name, from: api, to: local });
|
|
1195
|
+
}
|
|
1196
|
+
}
|
|
1197
|
+
for (const name of apiMap.keys()) {
|
|
1198
|
+
if (!localMap.has(name)) removed.push(name);
|
|
1199
|
+
}
|
|
1200
|
+
added.sort((a, b) => a.name.localeCompare(b.name));
|
|
1201
|
+
removed.sort();
|
|
1202
|
+
changed.sort((a, b) => a.name.localeCompare(b.name));
|
|
1203
|
+
if (!added.length && !removed.length && !changed.length) return null;
|
|
1204
|
+
return { added, removed, changed };
|
|
1074
1205
|
}
|
|
1206
|
+
function diffTriggers(apiTriggers, localTriggers) {
|
|
1207
|
+
const apiMap = mapByName(apiTriggers);
|
|
1208
|
+
const localMap = mapByName(localTriggers);
|
|
1209
|
+
const added = [];
|
|
1210
|
+
const removed = [];
|
|
1211
|
+
const changed = [];
|
|
1212
|
+
for (const [name, local] of localMap.entries()) {
|
|
1213
|
+
if (!apiMap.has(name)) {
|
|
1214
|
+
added.push(local);
|
|
1215
|
+
continue;
|
|
1216
|
+
}
|
|
1217
|
+
const api = apiMap.get(name);
|
|
1218
|
+
if (api.event !== local.event || api.trigger !== local.trigger) {
|
|
1219
|
+
changed.push({ name, from: api, to: local });
|
|
1220
|
+
}
|
|
1221
|
+
}
|
|
1222
|
+
for (const name of apiMap.keys()) {
|
|
1223
|
+
if (!localMap.has(name)) removed.push(name);
|
|
1224
|
+
}
|
|
1225
|
+
added.sort((a, b) => a.name.localeCompare(b.name));
|
|
1226
|
+
removed.sort();
|
|
1227
|
+
changed.sort((a, b) => a.name.localeCompare(b.name));
|
|
1228
|
+
if (!added.length && !removed.length && !changed.length) return null;
|
|
1229
|
+
return { added, removed, changed };
|
|
1230
|
+
}
|
|
1231
|
+
function computeSchemaDiff(apiSchema, localSchema) {
|
|
1232
|
+
const apiEntities = normalizeEntities(apiSchema);
|
|
1233
|
+
const localEntities = normalizeEntities(localSchema);
|
|
1234
|
+
const apiMap = mapByName(apiEntities);
|
|
1235
|
+
const localMap = mapByName(localEntities);
|
|
1236
|
+
const newTables = [];
|
|
1237
|
+
const removedTables = [];
|
|
1238
|
+
const changedTables = [];
|
|
1239
|
+
for (const [name, localEntity] of localMap.entries()) {
|
|
1240
|
+
if (!apiMap.has(name)) {
|
|
1241
|
+
newTables.push(name);
|
|
1242
|
+
continue;
|
|
1243
|
+
}
|
|
1244
|
+
const apiEntity = apiMap.get(name);
|
|
1245
|
+
const tableDiff = { name };
|
|
1246
|
+
const partitionFrom = normalizePartition(apiEntity.partition);
|
|
1247
|
+
const partitionTo = normalizePartition(localEntity.partition);
|
|
1248
|
+
if (partitionFrom !== partitionTo) {
|
|
1249
|
+
tableDiff.partition = { from: partitionFrom || null, to: partitionTo || null };
|
|
1250
|
+
}
|
|
1251
|
+
if (!identifiersEqual(apiEntity.identifier, localEntity.identifier)) {
|
|
1252
|
+
tableDiff.identifier = {
|
|
1253
|
+
from: apiEntity.identifier ?? null,
|
|
1254
|
+
to: localEntity.identifier ?? null
|
|
1255
|
+
};
|
|
1256
|
+
}
|
|
1257
|
+
const attrs = diffAttributes(apiEntity.attributes, localEntity.attributes);
|
|
1258
|
+
if (attrs) tableDiff.attributes = attrs;
|
|
1259
|
+
const indexes = diffIndexes(apiEntity.indexes, localEntity.indexes);
|
|
1260
|
+
if (indexes) tableDiff.indexes = indexes;
|
|
1261
|
+
const resolvers = diffResolvers(apiEntity.resolvers, localEntity.resolvers);
|
|
1262
|
+
if (resolvers) tableDiff.resolvers = resolvers;
|
|
1263
|
+
const triggers = diffTriggers(apiEntity.triggers, localEntity.triggers);
|
|
1264
|
+
if (triggers) tableDiff.triggers = triggers;
|
|
1265
|
+
const hasChange = tableDiff.partition || tableDiff.identifier || tableDiff.attributes || tableDiff.indexes || tableDiff.resolvers || tableDiff.triggers;
|
|
1266
|
+
if (hasChange) {
|
|
1267
|
+
changedTables.push(tableDiff);
|
|
1268
|
+
}
|
|
1269
|
+
}
|
|
1270
|
+
for (const name of apiMap.keys()) {
|
|
1271
|
+
if (!localMap.has(name)) removedTables.push(name);
|
|
1272
|
+
}
|
|
1273
|
+
newTables.sort();
|
|
1274
|
+
removedTables.sort();
|
|
1275
|
+
changedTables.sort((a, b) => a.name.localeCompare(b.name));
|
|
1276
|
+
return { newTables, removedTables, changedTables };
|
|
1277
|
+
}
|
|
1278
|
+
|
|
1279
|
+
// src/impl/onyx-core.ts
|
|
1280
|
+
var DEFAULT_CACHE_TTL = 5 * 60 * 1e3;
|
|
1075
1281
|
function toSingleCondition(criteria) {
|
|
1076
1282
|
return { conditionType: "SingleCondition", criteria };
|
|
1077
1283
|
}
|
|
@@ -1125,7 +1331,20 @@ function normalizeDate(value) {
|
|
|
1125
1331
|
return Number.isNaN(ts.getTime()) ? void 0 : ts;
|
|
1126
1332
|
}
|
|
1127
1333
|
function normalizeSchemaRevision(input, fallbackDatabaseId) {
|
|
1128
|
-
const {
|
|
1334
|
+
const {
|
|
1335
|
+
meta,
|
|
1336
|
+
createdAt,
|
|
1337
|
+
publishedAt,
|
|
1338
|
+
revisionId,
|
|
1339
|
+
entityText,
|
|
1340
|
+
databaseId,
|
|
1341
|
+
entities,
|
|
1342
|
+
revisionDescription,
|
|
1343
|
+
...rest
|
|
1344
|
+
} = input;
|
|
1345
|
+
const dbId = typeof databaseId === "string" ? databaseId : fallbackDatabaseId;
|
|
1346
|
+
const entityList = Array.isArray(entities) ? entities : [];
|
|
1347
|
+
const revisionDesc = typeof revisionDescription === "string" ? revisionDescription : void 0;
|
|
1129
1348
|
const mergedMeta = {
|
|
1130
1349
|
revisionId: meta?.revisionId ?? revisionId,
|
|
1131
1350
|
createdAt: normalizeDate(meta?.createdAt ?? createdAt),
|
|
@@ -1133,10 +1352,11 @@ function normalizeSchemaRevision(input, fallbackDatabaseId) {
|
|
|
1133
1352
|
};
|
|
1134
1353
|
const cleanedMeta = mergedMeta.revisionId || mergedMeta.createdAt || mergedMeta.publishedAt ? mergedMeta : void 0;
|
|
1135
1354
|
return {
|
|
1136
|
-
|
|
1137
|
-
|
|
1355
|
+
databaseId: dbId,
|
|
1356
|
+
revisionDescription: revisionDesc,
|
|
1357
|
+
entities: entityList,
|
|
1138
1358
|
meta: cleanedMeta,
|
|
1139
|
-
|
|
1359
|
+
...rest
|
|
1140
1360
|
};
|
|
1141
1361
|
}
|
|
1142
1362
|
var OnyxDatabaseImpl = class {
|
|
@@ -1147,7 +1367,7 @@ var OnyxDatabaseImpl = class {
|
|
|
1147
1367
|
requestLoggingEnabled;
|
|
1148
1368
|
responseLoggingEnabled;
|
|
1149
1369
|
defaultPartition;
|
|
1150
|
-
constructor(config) {
|
|
1370
|
+
constructor(config, resolveConfigWithCache) {
|
|
1151
1371
|
this.requestLoggingEnabled = !!config?.requestLoggingEnabled;
|
|
1152
1372
|
this.responseLoggingEnabled = !!config?.responseLoggingEnabled;
|
|
1153
1373
|
this.defaultPartition = config?.partition;
|
|
@@ -1164,7 +1384,10 @@ var OnyxDatabaseImpl = class {
|
|
|
1164
1384
|
apiSecret: this.resolved.apiSecret,
|
|
1165
1385
|
fetchImpl: this.resolved.fetch,
|
|
1166
1386
|
requestLoggingEnabled: this.requestLoggingEnabled,
|
|
1167
|
-
responseLoggingEnabled: this.responseLoggingEnabled
|
|
1387
|
+
responseLoggingEnabled: this.responseLoggingEnabled,
|
|
1388
|
+
retryEnabled: this.resolved.retryEnabled,
|
|
1389
|
+
maxRetries: this.resolved.maxRetries,
|
|
1390
|
+
retryInitialDelayMs: this.resolved.retryInitialDelayMs
|
|
1168
1391
|
});
|
|
1169
1392
|
}
|
|
1170
1393
|
return {
|
|
@@ -1248,7 +1471,8 @@ var OnyxDatabaseImpl = class {
|
|
|
1248
1471
|
const path = `/data/${encodeURIComponent(databaseId)}/${encodeURIComponent(
|
|
1249
1472
|
table
|
|
1250
1473
|
)}/${encodeURIComponent(primaryKey)}${params.toString() ? `?${params.toString()}` : ""}`;
|
|
1251
|
-
|
|
1474
|
+
await http.request("DELETE", path);
|
|
1475
|
+
return true;
|
|
1252
1476
|
}
|
|
1253
1477
|
async saveDocument(doc) {
|
|
1254
1478
|
const { http, databaseId } = await this.ensureClient();
|
|
@@ -1291,6 +1515,10 @@ var OnyxDatabaseImpl = class {
|
|
|
1291
1515
|
const res = await http.request("GET", path);
|
|
1292
1516
|
return Array.isArray(res) ? res.map((entry) => normalizeSchemaRevision(entry, databaseId)) : [];
|
|
1293
1517
|
}
|
|
1518
|
+
async diffSchema(localSchema) {
|
|
1519
|
+
const apiSchema = await this.getSchema();
|
|
1520
|
+
return computeSchemaDiff(apiSchema, localSchema);
|
|
1521
|
+
}
|
|
1294
1522
|
async updateSchema(schema, options) {
|
|
1295
1523
|
const { http, databaseId } = await this.ensureClient();
|
|
1296
1524
|
const params = new URLSearchParams();
|
|
@@ -1624,7 +1852,6 @@ var QueryBuilderImpl = class {
|
|
|
1624
1852
|
}
|
|
1625
1853
|
async firstOrNull() {
|
|
1626
1854
|
if (this.mode !== "select") throw new Error("Cannot call firstOrNull() in update mode.");
|
|
1627
|
-
if (!this.conditions) throw new OnyxError("firstOrNull() requires a where() clause.");
|
|
1628
1855
|
this.limitValue = 1;
|
|
1629
1856
|
const pg = await this.page();
|
|
1630
1857
|
return Array.isArray(pg.records) && pg.records.length > 0 ? pg.records[0] : null;
|
|
@@ -1716,12 +1943,32 @@ var CascadeBuilderImpl = class {
|
|
|
1716
1943
|
return this.db.delete(table, primaryKey, opts);
|
|
1717
1944
|
}
|
|
1718
1945
|
};
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1946
|
+
function createOnyxFacade(resolveConfig2) {
|
|
1947
|
+
let cachedCfg = null;
|
|
1948
|
+
function resolveConfigWithCache(config) {
|
|
1949
|
+
const ttl = config?.ttl ?? DEFAULT_CACHE_TTL;
|
|
1950
|
+
const now = Date.now();
|
|
1951
|
+
if (cachedCfg && cachedCfg.expires > now) {
|
|
1952
|
+
return cachedCfg.promise;
|
|
1953
|
+
}
|
|
1954
|
+
const { ttl: _ttl, requestLoggingEnabled: _reqLog, responseLoggingEnabled: _resLog, ...rest } = config ?? {};
|
|
1955
|
+
const promise = resolveConfig2(rest);
|
|
1956
|
+
cachedCfg = { promise, expires: now + ttl };
|
|
1957
|
+
return promise;
|
|
1958
|
+
}
|
|
1959
|
+
function clearCacheConfig() {
|
|
1960
|
+
cachedCfg = null;
|
|
1961
|
+
}
|
|
1962
|
+
return {
|
|
1963
|
+
init(config) {
|
|
1964
|
+
return new OnyxDatabaseImpl(config, resolveConfigWithCache);
|
|
1965
|
+
},
|
|
1966
|
+
clearCacheConfig
|
|
1967
|
+
};
|
|
1968
|
+
}
|
|
1969
|
+
|
|
1970
|
+
// src/impl/onyx.ts
|
|
1971
|
+
var onyx = createOnyxFacade((config) => resolveConfig(config));
|
|
1725
1972
|
|
|
1726
1973
|
// gen/generate.ts
|
|
1727
1974
|
var DEFAULT_SCHEMA_PATH = "./onyx.schema.json";
|