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