@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/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
- cfgPath = await readConfigPath(configPath);
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
- project = await readProjectFile(targetId);
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
- home = await readHomeProfile(targetId);
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 { meta, createdAt, publishedAt, revisionId, entityText, ...rest } = input;
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
- ...rest,
1039
- databaseId: input.databaseId ?? fallbackDatabaseId,
1242
+ databaseId: dbId,
1243
+ revisionDescription: revisionDesc,
1244
+ entities: entityList,
1040
1245
  meta: cleanedMeta,
1041
- entities: input.entities ?? []
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
- return http.request("DELETE", path);
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();