@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
package/dist/index.js
CHANGED
|
@@ -67,7 +67,7 @@ function readEnv(targetId) {
|
|
|
67
67
|
return res;
|
|
68
68
|
}
|
|
69
69
|
async function readProjectFile(databaseId) {
|
|
70
|
-
if (!isNode) return {};
|
|
70
|
+
if (!isNode) return { config: {} };
|
|
71
71
|
const fs = await nodeImport("node:fs/promises");
|
|
72
72
|
const path = await nodeImport("node:path");
|
|
73
73
|
const cwd = gProcess?.cwd?.() ?? ".";
|
|
@@ -76,7 +76,7 @@ async function readProjectFile(databaseId) {
|
|
|
76
76
|
const sanitized = txt.replace(/[\r\n]+/g, "");
|
|
77
77
|
const json = dropUndefined(JSON.parse(sanitized));
|
|
78
78
|
dbg("project file:", p, "\u2192", mask(json));
|
|
79
|
-
return json;
|
|
79
|
+
return { config: json, path: p };
|
|
80
80
|
};
|
|
81
81
|
if (databaseId) {
|
|
82
82
|
const specific = path.resolve(cwd, `onyx-database-${databaseId}.json`);
|
|
@@ -91,11 +91,11 @@ async function readProjectFile(databaseId) {
|
|
|
91
91
|
return await tryRead(fallback);
|
|
92
92
|
} catch {
|
|
93
93
|
dbg("project file not found:", fallback);
|
|
94
|
-
return {};
|
|
94
|
+
return { config: {} };
|
|
95
95
|
}
|
|
96
96
|
}
|
|
97
97
|
async function readHomeProfile(databaseId) {
|
|
98
|
-
if (!isNode) return {};
|
|
98
|
+
if (!isNode) return { config: {} };
|
|
99
99
|
const fs = await nodeImport("node:fs/promises");
|
|
100
100
|
const os = await nodeImport("node:os");
|
|
101
101
|
const path = await nodeImport("node:path");
|
|
@@ -115,7 +115,7 @@ async function readHomeProfile(databaseId) {
|
|
|
115
115
|
const sanitized = txt.replace(/[\r\n]+/g, "");
|
|
116
116
|
const json = dropUndefined(JSON.parse(sanitized));
|
|
117
117
|
dbg("home profile used:", p, "\u2192", mask(json));
|
|
118
|
-
return json;
|
|
118
|
+
return { config: json, path: p };
|
|
119
119
|
} catch (e) {
|
|
120
120
|
const msg = e instanceof Error ? e.message : String(e);
|
|
121
121
|
throw new OnyxConfigError(`Failed to read ${p}: ${msg}`);
|
|
@@ -134,7 +134,7 @@ async function readHomeProfile(databaseId) {
|
|
|
134
134
|
dbg("no home-root fallback:", defaultInHome);
|
|
135
135
|
if (!await fileExists(dir)) {
|
|
136
136
|
dbg("~/.onyx does not exist:", dir);
|
|
137
|
-
return {};
|
|
137
|
+
return { config: {} };
|
|
138
138
|
}
|
|
139
139
|
const files = await fs.readdir(dir).catch(() => []);
|
|
140
140
|
const matches2 = files.filter((f) => f.startsWith("onyx-database-") && f.endsWith(".json"));
|
|
@@ -148,10 +148,10 @@ async function readHomeProfile(databaseId) {
|
|
|
148
148
|
);
|
|
149
149
|
}
|
|
150
150
|
dbg("no usable home profiles found in", dir);
|
|
151
|
-
return {};
|
|
151
|
+
return { config: {} };
|
|
152
152
|
}
|
|
153
153
|
async function readConfigPath(p) {
|
|
154
|
-
if (!isNode) return {};
|
|
154
|
+
if (!isNode) return { config: {} };
|
|
155
155
|
const fs = await nodeImport("node:fs/promises");
|
|
156
156
|
const path = await nodeImport("node:path");
|
|
157
157
|
const cwd = gProcess?.cwd?.() ?? ".";
|
|
@@ -161,7 +161,7 @@ async function readConfigPath(p) {
|
|
|
161
161
|
const sanitized = txt.replace(/[\r\n]+/g, "");
|
|
162
162
|
const json = dropUndefined(JSON.parse(sanitized));
|
|
163
163
|
dbg("config path:", resolved, "\u2192", mask(json));
|
|
164
|
-
return json;
|
|
164
|
+
return { config: json, path: resolved };
|
|
165
165
|
} catch (e) {
|
|
166
166
|
const msg = e instanceof Error ? e.message : String(e);
|
|
167
167
|
throw new OnyxConfigError(`Failed to read ${resolved}: ${msg}`);
|
|
@@ -172,7 +172,8 @@ async function resolveConfig(input) {
|
|
|
172
172
|
const env = readEnv(input?.databaseId);
|
|
173
173
|
let cfgPath = {};
|
|
174
174
|
if (configPath) {
|
|
175
|
-
|
|
175
|
+
const cfgRes = await readConfigPath(configPath);
|
|
176
|
+
cfgPath = cfgRes.config;
|
|
176
177
|
}
|
|
177
178
|
const targetId = input?.databaseId ?? env.databaseId ?? cfgPath.databaseId;
|
|
178
179
|
let haveDbId = !!(input?.databaseId ?? env.databaseId ?? cfgPath.databaseId);
|
|
@@ -180,14 +181,16 @@ async function resolveConfig(input) {
|
|
|
180
181
|
let haveApiSecret = !!(input?.apiSecret ?? env.apiSecret ?? cfgPath.apiSecret);
|
|
181
182
|
let project = {};
|
|
182
183
|
if (!(haveDbId && haveApiKey && haveApiSecret)) {
|
|
183
|
-
|
|
184
|
+
const projRes = await readProjectFile(targetId);
|
|
185
|
+
project = projRes.config;
|
|
184
186
|
if (project.databaseId) haveDbId = true;
|
|
185
187
|
if (project.apiKey) haveApiKey = true;
|
|
186
188
|
if (project.apiSecret) haveApiSecret = true;
|
|
187
189
|
}
|
|
188
190
|
let home = {};
|
|
189
191
|
if (!(haveDbId && haveApiKey && haveApiSecret)) {
|
|
190
|
-
|
|
192
|
+
const homeRes = await readHomeProfile(targetId);
|
|
193
|
+
home = homeRes.config;
|
|
191
194
|
}
|
|
192
195
|
const merged = {
|
|
193
196
|
baseUrl: DEFAULT_BASE_URL,
|
|
@@ -206,6 +209,10 @@ async function resolveConfig(input) {
|
|
|
206
209
|
const fetchImpl = merged.fetch ?? (typeof gfetch === "function" ? (u, i) => gfetch(u, i) : async () => {
|
|
207
210
|
throw new OnyxConfigError("No fetch available; provide OnyxConfig.fetch");
|
|
208
211
|
});
|
|
212
|
+
const retryConfig = input?.retry ?? env.retry ?? cfgPath.retry ?? project.retry ?? home.retry ?? {};
|
|
213
|
+
const retryEnabled = retryConfig.enabled ?? true;
|
|
214
|
+
const maxRetries = retryConfig.maxRetries ?? 3;
|
|
215
|
+
const retryInitialDelayMs = retryConfig.initialDelayMs ?? 300;
|
|
209
216
|
const missing = [];
|
|
210
217
|
if (!databaseId) missing.push("databaseId");
|
|
211
218
|
if (!apiKey) missing.push("apiKey");
|
|
@@ -228,7 +235,16 @@ async function resolveConfig(input) {
|
|
|
228
235
|
`Missing required config: ${missing.join(", ")}. Sources: ${sources.join(", ")}`
|
|
229
236
|
);
|
|
230
237
|
}
|
|
231
|
-
const resolved = {
|
|
238
|
+
const resolved = {
|
|
239
|
+
baseUrl,
|
|
240
|
+
databaseId,
|
|
241
|
+
apiKey,
|
|
242
|
+
apiSecret,
|
|
243
|
+
fetch: fetchImpl,
|
|
244
|
+
retryEnabled,
|
|
245
|
+
maxRetries,
|
|
246
|
+
retryInitialDelayMs
|
|
247
|
+
};
|
|
232
248
|
const source = {
|
|
233
249
|
databaseId: input?.databaseId ? "explicit config" : env.databaseId ? "env" : cfgPath.databaseId ? "env ONYX_CONFIG_PATH" : project.databaseId ? "project file" : home.databaseId ? "home profile" : "unknown",
|
|
234
250
|
apiKey: input?.apiKey ? "explicit config" : env.apiKey ? "env" : cfgPath.apiKey ? "env ONYX_CONFIG_PATH" : project.apiKey ? "project file" : home.apiKey ? "home profile" : "unknown",
|
|
@@ -285,7 +301,7 @@ function parseJsonAllowNaN(txt) {
|
|
|
285
301
|
return JSON.parse(fixed);
|
|
286
302
|
}
|
|
287
303
|
}
|
|
288
|
-
var HttpClient = class {
|
|
304
|
+
var HttpClient = class _HttpClient {
|
|
289
305
|
baseUrl;
|
|
290
306
|
apiKey;
|
|
291
307
|
apiSecret;
|
|
@@ -293,6 +309,25 @@ var HttpClient = class {
|
|
|
293
309
|
defaults;
|
|
294
310
|
requestLoggingEnabled;
|
|
295
311
|
responseLoggingEnabled;
|
|
312
|
+
retryEnabled;
|
|
313
|
+
maxRetries;
|
|
314
|
+
retryInitialDelayMs;
|
|
315
|
+
shouldRetry;
|
|
316
|
+
static parseRetryAfter(header) {
|
|
317
|
+
if (!header) return null;
|
|
318
|
+
const trimmed = header.trim();
|
|
319
|
+
if (trimmed === "") return null;
|
|
320
|
+
const seconds = Number(trimmed);
|
|
321
|
+
if (Number.isFinite(seconds)) {
|
|
322
|
+
return Math.max(0, seconds * 1e3);
|
|
323
|
+
}
|
|
324
|
+
const dateMs = Date.parse(trimmed);
|
|
325
|
+
if (!Number.isNaN(dateMs)) {
|
|
326
|
+
const now = Date.now();
|
|
327
|
+
return Math.max(0, dateMs - now);
|
|
328
|
+
}
|
|
329
|
+
return null;
|
|
330
|
+
}
|
|
296
331
|
constructor(opts) {
|
|
297
332
|
if (!opts.baseUrl || opts.baseUrl.trim() === "") {
|
|
298
333
|
throw new OnyxConfigError("baseUrl is required");
|
|
@@ -317,6 +352,10 @@ var HttpClient = class {
|
|
|
317
352
|
const envDebug = globalThis.process?.env?.ONYX_DEBUG === "true";
|
|
318
353
|
this.requestLoggingEnabled = !!opts.requestLoggingEnabled || envDebug;
|
|
319
354
|
this.responseLoggingEnabled = !!opts.responseLoggingEnabled || envDebug;
|
|
355
|
+
this.retryEnabled = opts.retryEnabled ?? true;
|
|
356
|
+
this.maxRetries = Math.max(0, opts.maxRetries ?? 2);
|
|
357
|
+
this.retryInitialDelayMs = Math.max(0, opts.retryInitialDelayMs ?? 100);
|
|
358
|
+
this.shouldRetry = (method, path) => method === "GET" || path.startsWith("/query/");
|
|
320
359
|
}
|
|
321
360
|
headers(extra) {
|
|
322
361
|
const extras = { ...extra ?? {} };
|
|
@@ -357,9 +396,8 @@ var HttpClient = class {
|
|
|
357
396
|
headers,
|
|
358
397
|
body: payload
|
|
359
398
|
};
|
|
360
|
-
const
|
|
361
|
-
const
|
|
362
|
-
const maxAttempts = canRetry ? 3 : 1;
|
|
399
|
+
const canRetry = this.retryEnabled && this.shouldRetry(method, path);
|
|
400
|
+
const maxAttempts = canRetry ? this.maxRetries + 1 : 1;
|
|
363
401
|
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
364
402
|
try {
|
|
365
403
|
const res = await this.fetchImpl(url, init);
|
|
@@ -377,7 +415,10 @@ var HttpClient = class {
|
|
|
377
415
|
if (!res.ok) {
|
|
378
416
|
const msg = typeof data === "object" && data !== null && "error" in data && typeof data.error?.message === "string" ? String(data.error.message) : `${res.status} ${res.statusText}`;
|
|
379
417
|
if (canRetry && res.status >= 500 && attempt + 1 < maxAttempts) {
|
|
380
|
-
|
|
418
|
+
const serverRetry = _HttpClient.parseRetryAfter(res.headers.get("retry-after"));
|
|
419
|
+
const backoff = this.retryInitialDelayMs * 2 ** attempt;
|
|
420
|
+
const delay = serverRetry ?? backoff;
|
|
421
|
+
await new Promise((r) => setTimeout(r, delay));
|
|
381
422
|
continue;
|
|
382
423
|
}
|
|
383
424
|
throw new OnyxHttpError(msg, res.status, res.statusText, data, raw);
|
|
@@ -386,7 +427,8 @@ var HttpClient = class {
|
|
|
386
427
|
} catch (err) {
|
|
387
428
|
const retryable = canRetry && (!(err instanceof OnyxHttpError) || err.status >= 500);
|
|
388
429
|
if (attempt + 1 < maxAttempts && retryable) {
|
|
389
|
-
|
|
430
|
+
const delay = this.retryInitialDelayMs * 2 ** attempt;
|
|
431
|
+
await new Promise((r) => setTimeout(r, delay));
|
|
390
432
|
continue;
|
|
391
433
|
}
|
|
392
434
|
throw err;
|
|
@@ -947,31 +989,196 @@ var CascadeRelationshipBuilder = class {
|
|
|
947
989
|
}
|
|
948
990
|
};
|
|
949
991
|
|
|
950
|
-
// src/
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
992
|
+
// src/helpers/schema-diff.ts
|
|
993
|
+
function mapByName(items) {
|
|
994
|
+
const map = /* @__PURE__ */ new Map();
|
|
995
|
+
for (const item of items ?? []) {
|
|
996
|
+
if (!item?.name) continue;
|
|
997
|
+
map.set(item.name, item);
|
|
998
|
+
}
|
|
999
|
+
return map;
|
|
1000
|
+
}
|
|
1001
|
+
function normalizeEntities(schema) {
|
|
1002
|
+
if (Array.isArray(schema.entities)) {
|
|
1003
|
+
return schema.entities ?? [];
|
|
1004
|
+
}
|
|
1005
|
+
const tables = schema.tables;
|
|
1006
|
+
if (!Array.isArray(tables)) return [];
|
|
1007
|
+
return tables.map((table) => ({
|
|
1008
|
+
name: table.name,
|
|
1009
|
+
attributes: table.attributes ?? []
|
|
1010
|
+
}));
|
|
1011
|
+
}
|
|
1012
|
+
function normalizePartition(partition) {
|
|
1013
|
+
if (partition == null) return "";
|
|
1014
|
+
const trimmed = partition.trim();
|
|
1015
|
+
return trimmed;
|
|
1016
|
+
}
|
|
1017
|
+
function identifiersEqual(a, b) {
|
|
1018
|
+
if (!a && !b) return true;
|
|
1019
|
+
if (!a || !b) return false;
|
|
1020
|
+
return a.name === b.name && a.generator === b.generator && a.type === b.type;
|
|
1021
|
+
}
|
|
1022
|
+
function diffAttributes(apiAttrs, localAttrs) {
|
|
1023
|
+
const apiMap = mapByName(apiAttrs);
|
|
1024
|
+
const localMap = mapByName(localAttrs);
|
|
1025
|
+
const added = [];
|
|
1026
|
+
const removed = [];
|
|
1027
|
+
const changed = [];
|
|
1028
|
+
for (const [name, local] of localMap.entries()) {
|
|
1029
|
+
if (!apiMap.has(name)) {
|
|
1030
|
+
added.push(local);
|
|
1031
|
+
continue;
|
|
1032
|
+
}
|
|
1033
|
+
const api = apiMap.get(name);
|
|
1034
|
+
const apiNull = Boolean(api.isNullable);
|
|
1035
|
+
const localNull = Boolean(local.isNullable);
|
|
1036
|
+
if (api.type !== local.type || apiNull !== localNull) {
|
|
1037
|
+
changed.push({
|
|
1038
|
+
name,
|
|
1039
|
+
from: { type: api.type, isNullable: apiNull },
|
|
1040
|
+
to: { type: local.type, isNullable: localNull }
|
|
1041
|
+
});
|
|
1042
|
+
}
|
|
955
1043
|
}
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
if (cachedCfg && cachedCfg.expires > now) {
|
|
965
|
-
return cachedCfg.promise;
|
|
966
|
-
}
|
|
967
|
-
const { ttl: _ttl, requestLoggingEnabled: _reqLog, responseLoggingEnabled: _resLog, ...rest } = config ?? {};
|
|
968
|
-
const promise = resolveConfig(rest);
|
|
969
|
-
cachedCfg = { promise, expires: now + ttl };
|
|
970
|
-
return promise;
|
|
1044
|
+
for (const name of apiMap.keys()) {
|
|
1045
|
+
if (!localMap.has(name)) removed.push(name);
|
|
1046
|
+
}
|
|
1047
|
+
added.sort((a, b) => a.name.localeCompare(b.name));
|
|
1048
|
+
removed.sort();
|
|
1049
|
+
changed.sort((a, b) => a.name.localeCompare(b.name));
|
|
1050
|
+
if (!added.length && !removed.length && !changed.length) return null;
|
|
1051
|
+
return { added, removed, changed };
|
|
971
1052
|
}
|
|
972
|
-
function
|
|
973
|
-
|
|
1053
|
+
function diffIndexes(apiIndexes, localIndexes) {
|
|
1054
|
+
const apiMap = mapByName(apiIndexes);
|
|
1055
|
+
const localMap = mapByName(localIndexes);
|
|
1056
|
+
const added = [];
|
|
1057
|
+
const removed = [];
|
|
1058
|
+
const changed = [];
|
|
1059
|
+
for (const [name, local] of localMap.entries()) {
|
|
1060
|
+
if (!apiMap.has(name)) {
|
|
1061
|
+
added.push(local);
|
|
1062
|
+
continue;
|
|
1063
|
+
}
|
|
1064
|
+
const api = apiMap.get(name);
|
|
1065
|
+
const apiType = api.type ?? "DEFAULT";
|
|
1066
|
+
const localType = local.type ?? "DEFAULT";
|
|
1067
|
+
const apiScore = api.minimumScore;
|
|
1068
|
+
const localScore = local.minimumScore;
|
|
1069
|
+
if (apiType !== localType || apiScore !== localScore) {
|
|
1070
|
+
changed.push({ name, from: api, to: local });
|
|
1071
|
+
}
|
|
1072
|
+
}
|
|
1073
|
+
for (const name of apiMap.keys()) {
|
|
1074
|
+
if (!localMap.has(name)) removed.push(name);
|
|
1075
|
+
}
|
|
1076
|
+
added.sort((a, b) => a.name.localeCompare(b.name));
|
|
1077
|
+
removed.sort();
|
|
1078
|
+
changed.sort((a, b) => a.name.localeCompare(b.name));
|
|
1079
|
+
if (!added.length && !removed.length && !changed.length) return null;
|
|
1080
|
+
return { added, removed, changed };
|
|
1081
|
+
}
|
|
1082
|
+
function diffResolvers(apiResolvers, localResolvers) {
|
|
1083
|
+
const apiMap = mapByName(apiResolvers);
|
|
1084
|
+
const localMap = mapByName(localResolvers);
|
|
1085
|
+
const added = [];
|
|
1086
|
+
const removed = [];
|
|
1087
|
+
const changed = [];
|
|
1088
|
+
for (const [name, local] of localMap.entries()) {
|
|
1089
|
+
if (!apiMap.has(name)) {
|
|
1090
|
+
added.push(local);
|
|
1091
|
+
continue;
|
|
1092
|
+
}
|
|
1093
|
+
const api = apiMap.get(name);
|
|
1094
|
+
if (api.resolver !== local.resolver) {
|
|
1095
|
+
changed.push({ name, from: api, to: local });
|
|
1096
|
+
}
|
|
1097
|
+
}
|
|
1098
|
+
for (const name of apiMap.keys()) {
|
|
1099
|
+
if (!localMap.has(name)) removed.push(name);
|
|
1100
|
+
}
|
|
1101
|
+
added.sort((a, b) => a.name.localeCompare(b.name));
|
|
1102
|
+
removed.sort();
|
|
1103
|
+
changed.sort((a, b) => a.name.localeCompare(b.name));
|
|
1104
|
+
if (!added.length && !removed.length && !changed.length) return null;
|
|
1105
|
+
return { added, removed, changed };
|
|
974
1106
|
}
|
|
1107
|
+
function diffTriggers(apiTriggers, localTriggers) {
|
|
1108
|
+
const apiMap = mapByName(apiTriggers);
|
|
1109
|
+
const localMap = mapByName(localTriggers);
|
|
1110
|
+
const added = [];
|
|
1111
|
+
const removed = [];
|
|
1112
|
+
const changed = [];
|
|
1113
|
+
for (const [name, local] of localMap.entries()) {
|
|
1114
|
+
if (!apiMap.has(name)) {
|
|
1115
|
+
added.push(local);
|
|
1116
|
+
continue;
|
|
1117
|
+
}
|
|
1118
|
+
const api = apiMap.get(name);
|
|
1119
|
+
if (api.event !== local.event || api.trigger !== local.trigger) {
|
|
1120
|
+
changed.push({ name, from: api, to: local });
|
|
1121
|
+
}
|
|
1122
|
+
}
|
|
1123
|
+
for (const name of apiMap.keys()) {
|
|
1124
|
+
if (!localMap.has(name)) removed.push(name);
|
|
1125
|
+
}
|
|
1126
|
+
added.sort((a, b) => a.name.localeCompare(b.name));
|
|
1127
|
+
removed.sort();
|
|
1128
|
+
changed.sort((a, b) => a.name.localeCompare(b.name));
|
|
1129
|
+
if (!added.length && !removed.length && !changed.length) return null;
|
|
1130
|
+
return { added, removed, changed };
|
|
1131
|
+
}
|
|
1132
|
+
function computeSchemaDiff(apiSchema, localSchema) {
|
|
1133
|
+
const apiEntities = normalizeEntities(apiSchema);
|
|
1134
|
+
const localEntities = normalizeEntities(localSchema);
|
|
1135
|
+
const apiMap = mapByName(apiEntities);
|
|
1136
|
+
const localMap = mapByName(localEntities);
|
|
1137
|
+
const newTables = [];
|
|
1138
|
+
const removedTables = [];
|
|
1139
|
+
const changedTables = [];
|
|
1140
|
+
for (const [name, localEntity] of localMap.entries()) {
|
|
1141
|
+
if (!apiMap.has(name)) {
|
|
1142
|
+
newTables.push(name);
|
|
1143
|
+
continue;
|
|
1144
|
+
}
|
|
1145
|
+
const apiEntity = apiMap.get(name);
|
|
1146
|
+
const tableDiff = { name };
|
|
1147
|
+
const partitionFrom = normalizePartition(apiEntity.partition);
|
|
1148
|
+
const partitionTo = normalizePartition(localEntity.partition);
|
|
1149
|
+
if (partitionFrom !== partitionTo) {
|
|
1150
|
+
tableDiff.partition = { from: partitionFrom || null, to: partitionTo || null };
|
|
1151
|
+
}
|
|
1152
|
+
if (!identifiersEqual(apiEntity.identifier, localEntity.identifier)) {
|
|
1153
|
+
tableDiff.identifier = {
|
|
1154
|
+
from: apiEntity.identifier ?? null,
|
|
1155
|
+
to: localEntity.identifier ?? null
|
|
1156
|
+
};
|
|
1157
|
+
}
|
|
1158
|
+
const attrs = diffAttributes(apiEntity.attributes, localEntity.attributes);
|
|
1159
|
+
if (attrs) tableDiff.attributes = attrs;
|
|
1160
|
+
const indexes = diffIndexes(apiEntity.indexes, localEntity.indexes);
|
|
1161
|
+
if (indexes) tableDiff.indexes = indexes;
|
|
1162
|
+
const resolvers = diffResolvers(apiEntity.resolvers, localEntity.resolvers);
|
|
1163
|
+
if (resolvers) tableDiff.resolvers = resolvers;
|
|
1164
|
+
const triggers = diffTriggers(apiEntity.triggers, localEntity.triggers);
|
|
1165
|
+
if (triggers) tableDiff.triggers = triggers;
|
|
1166
|
+
const hasChange = tableDiff.partition || tableDiff.identifier || tableDiff.attributes || tableDiff.indexes || tableDiff.resolvers || tableDiff.triggers;
|
|
1167
|
+
if (hasChange) {
|
|
1168
|
+
changedTables.push(tableDiff);
|
|
1169
|
+
}
|
|
1170
|
+
}
|
|
1171
|
+
for (const name of apiMap.keys()) {
|
|
1172
|
+
if (!localMap.has(name)) removedTables.push(name);
|
|
1173
|
+
}
|
|
1174
|
+
newTables.sort();
|
|
1175
|
+
removedTables.sort();
|
|
1176
|
+
changedTables.sort((a, b) => a.name.localeCompare(b.name));
|
|
1177
|
+
return { newTables, removedTables, changedTables };
|
|
1178
|
+
}
|
|
1179
|
+
|
|
1180
|
+
// src/impl/onyx-core.ts
|
|
1181
|
+
var DEFAULT_CACHE_TTL = 5 * 60 * 1e3;
|
|
975
1182
|
function toSingleCondition(criteria) {
|
|
976
1183
|
return { conditionType: "SingleCondition", criteria };
|
|
977
1184
|
}
|
|
@@ -1025,7 +1232,20 @@ function normalizeDate(value) {
|
|
|
1025
1232
|
return Number.isNaN(ts.getTime()) ? void 0 : ts;
|
|
1026
1233
|
}
|
|
1027
1234
|
function normalizeSchemaRevision(input, fallbackDatabaseId) {
|
|
1028
|
-
const {
|
|
1235
|
+
const {
|
|
1236
|
+
meta,
|
|
1237
|
+
createdAt,
|
|
1238
|
+
publishedAt,
|
|
1239
|
+
revisionId,
|
|
1240
|
+
entityText,
|
|
1241
|
+
databaseId,
|
|
1242
|
+
entities,
|
|
1243
|
+
revisionDescription,
|
|
1244
|
+
...rest
|
|
1245
|
+
} = input;
|
|
1246
|
+
const dbId = typeof databaseId === "string" ? databaseId : fallbackDatabaseId;
|
|
1247
|
+
const entityList = Array.isArray(entities) ? entities : [];
|
|
1248
|
+
const revisionDesc = typeof revisionDescription === "string" ? revisionDescription : void 0;
|
|
1029
1249
|
const mergedMeta = {
|
|
1030
1250
|
revisionId: meta?.revisionId ?? revisionId,
|
|
1031
1251
|
createdAt: normalizeDate(meta?.createdAt ?? createdAt),
|
|
@@ -1033,10 +1253,11 @@ function normalizeSchemaRevision(input, fallbackDatabaseId) {
|
|
|
1033
1253
|
};
|
|
1034
1254
|
const cleanedMeta = mergedMeta.revisionId || mergedMeta.createdAt || mergedMeta.publishedAt ? mergedMeta : void 0;
|
|
1035
1255
|
return {
|
|
1036
|
-
|
|
1037
|
-
|
|
1256
|
+
databaseId: dbId,
|
|
1257
|
+
revisionDescription: revisionDesc,
|
|
1258
|
+
entities: entityList,
|
|
1038
1259
|
meta: cleanedMeta,
|
|
1039
|
-
|
|
1260
|
+
...rest
|
|
1040
1261
|
};
|
|
1041
1262
|
}
|
|
1042
1263
|
var OnyxDatabaseImpl = class {
|
|
@@ -1047,7 +1268,7 @@ var OnyxDatabaseImpl = class {
|
|
|
1047
1268
|
requestLoggingEnabled;
|
|
1048
1269
|
responseLoggingEnabled;
|
|
1049
1270
|
defaultPartition;
|
|
1050
|
-
constructor(config) {
|
|
1271
|
+
constructor(config, resolveConfigWithCache) {
|
|
1051
1272
|
this.requestLoggingEnabled = !!config?.requestLoggingEnabled;
|
|
1052
1273
|
this.responseLoggingEnabled = !!config?.responseLoggingEnabled;
|
|
1053
1274
|
this.defaultPartition = config?.partition;
|
|
@@ -1064,7 +1285,10 @@ var OnyxDatabaseImpl = class {
|
|
|
1064
1285
|
apiSecret: this.resolved.apiSecret,
|
|
1065
1286
|
fetchImpl: this.resolved.fetch,
|
|
1066
1287
|
requestLoggingEnabled: this.requestLoggingEnabled,
|
|
1067
|
-
responseLoggingEnabled: this.responseLoggingEnabled
|
|
1288
|
+
responseLoggingEnabled: this.responseLoggingEnabled,
|
|
1289
|
+
retryEnabled: this.resolved.retryEnabled,
|
|
1290
|
+
maxRetries: this.resolved.maxRetries,
|
|
1291
|
+
retryInitialDelayMs: this.resolved.retryInitialDelayMs
|
|
1068
1292
|
});
|
|
1069
1293
|
}
|
|
1070
1294
|
return {
|
|
@@ -1148,7 +1372,8 @@ var OnyxDatabaseImpl = class {
|
|
|
1148
1372
|
const path = `/data/${encodeURIComponent(databaseId)}/${encodeURIComponent(
|
|
1149
1373
|
table
|
|
1150
1374
|
)}/${encodeURIComponent(primaryKey)}${params.toString() ? `?${params.toString()}` : ""}`;
|
|
1151
|
-
|
|
1375
|
+
await http.request("DELETE", path);
|
|
1376
|
+
return true;
|
|
1152
1377
|
}
|
|
1153
1378
|
async saveDocument(doc) {
|
|
1154
1379
|
const { http, databaseId } = await this.ensureClient();
|
|
@@ -1191,6 +1416,10 @@ var OnyxDatabaseImpl = class {
|
|
|
1191
1416
|
const res = await http.request("GET", path);
|
|
1192
1417
|
return Array.isArray(res) ? res.map((entry) => normalizeSchemaRevision(entry, databaseId)) : [];
|
|
1193
1418
|
}
|
|
1419
|
+
async diffSchema(localSchema) {
|
|
1420
|
+
const apiSchema = await this.getSchema();
|
|
1421
|
+
return computeSchemaDiff(apiSchema, localSchema);
|
|
1422
|
+
}
|
|
1194
1423
|
async updateSchema(schema, options) {
|
|
1195
1424
|
const { http, databaseId } = await this.ensureClient();
|
|
1196
1425
|
const params = new URLSearchParams();
|
|
@@ -1524,7 +1753,6 @@ var QueryBuilderImpl = class {
|
|
|
1524
1753
|
}
|
|
1525
1754
|
async firstOrNull() {
|
|
1526
1755
|
if (this.mode !== "select") throw new Error("Cannot call firstOrNull() in update mode.");
|
|
1527
|
-
if (!this.conditions) throw new OnyxError("firstOrNull() requires a where() clause.");
|
|
1528
1756
|
this.limitValue = 1;
|
|
1529
1757
|
const pg = await this.page();
|
|
1530
1758
|
return Array.isArray(pg.records) && pg.records.length > 0 ? pg.records[0] : null;
|
|
@@ -1616,12 +1844,32 @@ var CascadeBuilderImpl = class {
|
|
|
1616
1844
|
return this.db.delete(table, primaryKey, opts);
|
|
1617
1845
|
}
|
|
1618
1846
|
};
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1847
|
+
function createOnyxFacade(resolveConfig2) {
|
|
1848
|
+
let cachedCfg = null;
|
|
1849
|
+
function resolveConfigWithCache(config) {
|
|
1850
|
+
const ttl = config?.ttl ?? DEFAULT_CACHE_TTL;
|
|
1851
|
+
const now = Date.now();
|
|
1852
|
+
if (cachedCfg && cachedCfg.expires > now) {
|
|
1853
|
+
return cachedCfg.promise;
|
|
1854
|
+
}
|
|
1855
|
+
const { ttl: _ttl, requestLoggingEnabled: _reqLog, responseLoggingEnabled: _resLog, ...rest } = config ?? {};
|
|
1856
|
+
const promise = resolveConfig2(rest);
|
|
1857
|
+
cachedCfg = { promise, expires: now + ttl };
|
|
1858
|
+
return promise;
|
|
1859
|
+
}
|
|
1860
|
+
function clearCacheConfig() {
|
|
1861
|
+
cachedCfg = null;
|
|
1862
|
+
}
|
|
1863
|
+
return {
|
|
1864
|
+
init(config) {
|
|
1865
|
+
return new OnyxDatabaseImpl(config, resolveConfigWithCache);
|
|
1866
|
+
},
|
|
1867
|
+
clearCacheConfig
|
|
1868
|
+
};
|
|
1869
|
+
}
|
|
1870
|
+
|
|
1871
|
+
// src/impl/onyx.ts
|
|
1872
|
+
var onyx = createOnyxFacade((config) => resolveConfig(config));
|
|
1625
1873
|
|
|
1626
1874
|
// src/helpers/sort.ts
|
|
1627
1875
|
var asc = (field) => ({ field, order: "ASC" });
|