@onyx.dev/onyx-database 1.0.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 +30 -1
- package/dist/gen/cli/generate.cjs +227 -17
- package/dist/gen/cli/generate.cjs.map +1 -1
- package/dist/index.cjs +227 -17
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +86 -4
- package/dist/index.d.ts +86 -4
- package/dist/index.js +227 -17
- package/dist/index.js.map +1 -1
- package/dist/schema/cli/schema.cjs +401 -233
- package/dist/schema/cli/schema.cjs.map +1 -1
- package/package.json +1 -1
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,
|
|
@@ -957,6 +960,194 @@ var OnyxError = class extends Error {
|
|
|
957
960
|
}
|
|
958
961
|
};
|
|
959
962
|
|
|
963
|
+
// src/helpers/schema-diff.ts
|
|
964
|
+
function mapByName(items) {
|
|
965
|
+
const map = /* @__PURE__ */ new Map();
|
|
966
|
+
for (const item of items ?? []) {
|
|
967
|
+
if (!item?.name) continue;
|
|
968
|
+
map.set(item.name, item);
|
|
969
|
+
}
|
|
970
|
+
return map;
|
|
971
|
+
}
|
|
972
|
+
function normalizeEntities(schema) {
|
|
973
|
+
if (Array.isArray(schema.entities)) {
|
|
974
|
+
return schema.entities ?? [];
|
|
975
|
+
}
|
|
976
|
+
const tables = schema.tables;
|
|
977
|
+
if (!Array.isArray(tables)) return [];
|
|
978
|
+
return tables.map((table) => ({
|
|
979
|
+
name: table.name,
|
|
980
|
+
attributes: table.attributes ?? []
|
|
981
|
+
}));
|
|
982
|
+
}
|
|
983
|
+
function normalizePartition(partition) {
|
|
984
|
+
if (partition == null) return "";
|
|
985
|
+
const trimmed = partition.trim();
|
|
986
|
+
return trimmed;
|
|
987
|
+
}
|
|
988
|
+
function identifiersEqual(a, b) {
|
|
989
|
+
if (!a && !b) return true;
|
|
990
|
+
if (!a || !b) return false;
|
|
991
|
+
return a.name === b.name && a.generator === b.generator && a.type === b.type;
|
|
992
|
+
}
|
|
993
|
+
function diffAttributes(apiAttrs, localAttrs) {
|
|
994
|
+
const apiMap = mapByName(apiAttrs);
|
|
995
|
+
const localMap = mapByName(localAttrs);
|
|
996
|
+
const added = [];
|
|
997
|
+
const removed = [];
|
|
998
|
+
const changed = [];
|
|
999
|
+
for (const [name, local] of localMap.entries()) {
|
|
1000
|
+
if (!apiMap.has(name)) {
|
|
1001
|
+
added.push(local);
|
|
1002
|
+
continue;
|
|
1003
|
+
}
|
|
1004
|
+
const api = apiMap.get(name);
|
|
1005
|
+
const apiNull = Boolean(api.isNullable);
|
|
1006
|
+
const localNull = Boolean(local.isNullable);
|
|
1007
|
+
if (api.type !== local.type || apiNull !== localNull) {
|
|
1008
|
+
changed.push({
|
|
1009
|
+
name,
|
|
1010
|
+
from: { type: api.type, isNullable: apiNull },
|
|
1011
|
+
to: { type: local.type, isNullable: localNull }
|
|
1012
|
+
});
|
|
1013
|
+
}
|
|
1014
|
+
}
|
|
1015
|
+
for (const name of apiMap.keys()) {
|
|
1016
|
+
if (!localMap.has(name)) removed.push(name);
|
|
1017
|
+
}
|
|
1018
|
+
added.sort((a, b) => a.name.localeCompare(b.name));
|
|
1019
|
+
removed.sort();
|
|
1020
|
+
changed.sort((a, b) => a.name.localeCompare(b.name));
|
|
1021
|
+
if (!added.length && !removed.length && !changed.length) return null;
|
|
1022
|
+
return { added, removed, changed };
|
|
1023
|
+
}
|
|
1024
|
+
function diffIndexes(apiIndexes, localIndexes) {
|
|
1025
|
+
const apiMap = mapByName(apiIndexes);
|
|
1026
|
+
const localMap = mapByName(localIndexes);
|
|
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 apiType = api.type ?? "DEFAULT";
|
|
1037
|
+
const localType = local.type ?? "DEFAULT";
|
|
1038
|
+
const apiScore = api.minimumScore;
|
|
1039
|
+
const localScore = local.minimumScore;
|
|
1040
|
+
if (apiType !== localType || apiScore !== localScore) {
|
|
1041
|
+
changed.push({ name, from: api, to: local });
|
|
1042
|
+
}
|
|
1043
|
+
}
|
|
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 };
|
|
1052
|
+
}
|
|
1053
|
+
function diffResolvers(apiResolvers, localResolvers) {
|
|
1054
|
+
const apiMap = mapByName(apiResolvers);
|
|
1055
|
+
const localMap = mapByName(localResolvers);
|
|
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
|
+
if (api.resolver !== local.resolver) {
|
|
1066
|
+
changed.push({ name, from: api, to: local });
|
|
1067
|
+
}
|
|
1068
|
+
}
|
|
1069
|
+
for (const name of apiMap.keys()) {
|
|
1070
|
+
if (!localMap.has(name)) removed.push(name);
|
|
1071
|
+
}
|
|
1072
|
+
added.sort((a, b) => a.name.localeCompare(b.name));
|
|
1073
|
+
removed.sort();
|
|
1074
|
+
changed.sort((a, b) => a.name.localeCompare(b.name));
|
|
1075
|
+
if (!added.length && !removed.length && !changed.length) return null;
|
|
1076
|
+
return { added, removed, changed };
|
|
1077
|
+
}
|
|
1078
|
+
function diffTriggers(apiTriggers, localTriggers) {
|
|
1079
|
+
const apiMap = mapByName(apiTriggers);
|
|
1080
|
+
const localMap = mapByName(localTriggers);
|
|
1081
|
+
const added = [];
|
|
1082
|
+
const removed = [];
|
|
1083
|
+
const changed = [];
|
|
1084
|
+
for (const [name, local] of localMap.entries()) {
|
|
1085
|
+
if (!apiMap.has(name)) {
|
|
1086
|
+
added.push(local);
|
|
1087
|
+
continue;
|
|
1088
|
+
}
|
|
1089
|
+
const api = apiMap.get(name);
|
|
1090
|
+
if (api.event !== local.event || api.trigger !== local.trigger) {
|
|
1091
|
+
changed.push({ name, from: api, to: local });
|
|
1092
|
+
}
|
|
1093
|
+
}
|
|
1094
|
+
for (const name of apiMap.keys()) {
|
|
1095
|
+
if (!localMap.has(name)) removed.push(name);
|
|
1096
|
+
}
|
|
1097
|
+
added.sort((a, b) => a.name.localeCompare(b.name));
|
|
1098
|
+
removed.sort();
|
|
1099
|
+
changed.sort((a, b) => a.name.localeCompare(b.name));
|
|
1100
|
+
if (!added.length && !removed.length && !changed.length) return null;
|
|
1101
|
+
return { added, removed, changed };
|
|
1102
|
+
}
|
|
1103
|
+
function computeSchemaDiff(apiSchema, localSchema) {
|
|
1104
|
+
const apiEntities = normalizeEntities(apiSchema);
|
|
1105
|
+
const localEntities = normalizeEntities(localSchema);
|
|
1106
|
+
const apiMap = mapByName(apiEntities);
|
|
1107
|
+
const localMap = mapByName(localEntities);
|
|
1108
|
+
const newTables = [];
|
|
1109
|
+
const removedTables = [];
|
|
1110
|
+
const changedTables = [];
|
|
1111
|
+
for (const [name, localEntity] of localMap.entries()) {
|
|
1112
|
+
if (!apiMap.has(name)) {
|
|
1113
|
+
newTables.push(name);
|
|
1114
|
+
continue;
|
|
1115
|
+
}
|
|
1116
|
+
const apiEntity = apiMap.get(name);
|
|
1117
|
+
const tableDiff = { name };
|
|
1118
|
+
const partitionFrom = normalizePartition(apiEntity.partition);
|
|
1119
|
+
const partitionTo = normalizePartition(localEntity.partition);
|
|
1120
|
+
if (partitionFrom !== partitionTo) {
|
|
1121
|
+
tableDiff.partition = { from: partitionFrom || null, to: partitionTo || null };
|
|
1122
|
+
}
|
|
1123
|
+
if (!identifiersEqual(apiEntity.identifier, localEntity.identifier)) {
|
|
1124
|
+
tableDiff.identifier = {
|
|
1125
|
+
from: apiEntity.identifier ?? null,
|
|
1126
|
+
to: localEntity.identifier ?? null
|
|
1127
|
+
};
|
|
1128
|
+
}
|
|
1129
|
+
const attrs = diffAttributes(apiEntity.attributes, localEntity.attributes);
|
|
1130
|
+
if (attrs) tableDiff.attributes = attrs;
|
|
1131
|
+
const indexes = diffIndexes(apiEntity.indexes, localEntity.indexes);
|
|
1132
|
+
if (indexes) tableDiff.indexes = indexes;
|
|
1133
|
+
const resolvers = diffResolvers(apiEntity.resolvers, localEntity.resolvers);
|
|
1134
|
+
if (resolvers) tableDiff.resolvers = resolvers;
|
|
1135
|
+
const triggers = diffTriggers(apiEntity.triggers, localEntity.triggers);
|
|
1136
|
+
if (triggers) tableDiff.triggers = triggers;
|
|
1137
|
+
const hasChange = tableDiff.partition || tableDiff.identifier || tableDiff.attributes || tableDiff.indexes || tableDiff.resolvers || tableDiff.triggers;
|
|
1138
|
+
if (hasChange) {
|
|
1139
|
+
changedTables.push(tableDiff);
|
|
1140
|
+
}
|
|
1141
|
+
}
|
|
1142
|
+
for (const name of apiMap.keys()) {
|
|
1143
|
+
if (!localMap.has(name)) removedTables.push(name);
|
|
1144
|
+
}
|
|
1145
|
+
newTables.sort();
|
|
1146
|
+
removedTables.sort();
|
|
1147
|
+
changedTables.sort((a, b) => a.name.localeCompare(b.name));
|
|
1148
|
+
return { newTables, removedTables, changedTables };
|
|
1149
|
+
}
|
|
1150
|
+
|
|
960
1151
|
// src/impl/onyx.ts
|
|
961
1152
|
var DEFAULT_CACHE_TTL = 5 * 60 * 1e3;
|
|
962
1153
|
var cachedCfg = null;
|
|
@@ -1027,7 +1218,20 @@ function normalizeDate(value) {
|
|
|
1027
1218
|
return Number.isNaN(ts.getTime()) ? void 0 : ts;
|
|
1028
1219
|
}
|
|
1029
1220
|
function normalizeSchemaRevision(input, fallbackDatabaseId) {
|
|
1030
|
-
const {
|
|
1221
|
+
const {
|
|
1222
|
+
meta,
|
|
1223
|
+
createdAt,
|
|
1224
|
+
publishedAt,
|
|
1225
|
+
revisionId,
|
|
1226
|
+
entityText,
|
|
1227
|
+
databaseId,
|
|
1228
|
+
entities,
|
|
1229
|
+
revisionDescription,
|
|
1230
|
+
...rest
|
|
1231
|
+
} = input;
|
|
1232
|
+
const dbId = typeof databaseId === "string" ? databaseId : fallbackDatabaseId;
|
|
1233
|
+
const entityList = Array.isArray(entities) ? entities : [];
|
|
1234
|
+
const revisionDesc = typeof revisionDescription === "string" ? revisionDescription : void 0;
|
|
1031
1235
|
const mergedMeta = {
|
|
1032
1236
|
revisionId: meta?.revisionId ?? revisionId,
|
|
1033
1237
|
createdAt: normalizeDate(meta?.createdAt ?? createdAt),
|
|
@@ -1035,10 +1239,11 @@ function normalizeSchemaRevision(input, fallbackDatabaseId) {
|
|
|
1035
1239
|
};
|
|
1036
1240
|
const cleanedMeta = mergedMeta.revisionId || mergedMeta.createdAt || mergedMeta.publishedAt ? mergedMeta : void 0;
|
|
1037
1241
|
return {
|
|
1038
|
-
|
|
1039
|
-
|
|
1242
|
+
databaseId: dbId,
|
|
1243
|
+
revisionDescription: revisionDesc,
|
|
1244
|
+
entities: entityList,
|
|
1040
1245
|
meta: cleanedMeta,
|
|
1041
|
-
|
|
1246
|
+
...rest
|
|
1042
1247
|
};
|
|
1043
1248
|
}
|
|
1044
1249
|
var OnyxDatabaseImpl = class {
|
|
@@ -1150,7 +1355,8 @@ var OnyxDatabaseImpl = class {
|
|
|
1150
1355
|
const path = `/data/${encodeURIComponent(databaseId)}/${encodeURIComponent(
|
|
1151
1356
|
table
|
|
1152
1357
|
)}/${encodeURIComponent(primaryKey)}${params.toString() ? `?${params.toString()}` : ""}`;
|
|
1153
|
-
|
|
1358
|
+
await http.request("DELETE", path);
|
|
1359
|
+
return true;
|
|
1154
1360
|
}
|
|
1155
1361
|
async saveDocument(doc) {
|
|
1156
1362
|
const { http, databaseId } = await this.ensureClient();
|
|
@@ -1193,6 +1399,10 @@ var OnyxDatabaseImpl = class {
|
|
|
1193
1399
|
const res = await http.request("GET", path);
|
|
1194
1400
|
return Array.isArray(res) ? res.map((entry) => normalizeSchemaRevision(entry, databaseId)) : [];
|
|
1195
1401
|
}
|
|
1402
|
+
async diffSchema(localSchema) {
|
|
1403
|
+
const apiSchema = await this.getSchema();
|
|
1404
|
+
return computeSchemaDiff(apiSchema, localSchema);
|
|
1405
|
+
}
|
|
1196
1406
|
async updateSchema(schema, options) {
|
|
1197
1407
|
const { http, databaseId } = await this.ensureClient();
|
|
1198
1408
|
const params = new URLSearchParams();
|