@objectstack/core 12.1.0 → 12.3.0

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
@@ -66,6 +66,7 @@ __export(index_exports, {
66
66
  createMemoryQueue: () => createMemoryQueue,
67
67
  createPluginConfigValidator: () => createPluginConfigValidator,
68
68
  createPluginPermissionEnforcer: () => createPluginPermissionEnforcer,
69
+ deepMerge: () => deepMerge,
69
70
  evaluateAuthGate: () => evaluateAuthGate,
70
71
  extractApiKey: () => extractApiKey,
71
72
  generateApiKey: () => generateApiKey,
@@ -78,6 +79,7 @@ __export(index_exports, {
78
79
  isNode: () => isNode,
79
80
  parseScopes: () => parseScopes,
80
81
  parseSignature: () => parseSignature,
82
+ readAuthoredTranslationLayer: () => readAuthoredTranslationLayer,
81
83
  resolveApiKeyPrincipal: () => resolveApiKeyPrincipal,
82
84
  resolveAuthzContext: () => resolveAuthzContext,
83
85
  resolveLocale: () => resolveLocale,
@@ -87,7 +89,8 @@ __export(index_exports, {
87
89
  verifyPayload: () => verifyPayload,
88
90
  verifyPlatformSignature: () => verifyPlatformSignature,
89
91
  verifyPluginArtifact: () => verifyPluginArtifact,
90
- verifyPublisherSignature: () => verifyPublisherSignature
92
+ verifyPublisherSignature: () => verifyPublisherSignature,
93
+ wireAuthoredTranslationSync: () => wireAuthoredTranslationSync
91
94
  });
92
95
  module.exports = __toCommonJS(index_exports);
93
96
 
@@ -1142,6 +1145,7 @@ function resolveLocale(requestedLocale, availableLocales) {
1142
1145
  }
1143
1146
  function createMemoryI18n() {
1144
1147
  const translations = /* @__PURE__ */ new Map();
1148
+ const authored = /* @__PURE__ */ new Map();
1145
1149
  let defaultLocale = "en";
1146
1150
  function resolveKey(data, key) {
1147
1151
  const parts = key.split(".");
@@ -1152,17 +1156,25 @@ function createMemoryI18n() {
1152
1156
  }
1153
1157
  return typeof current === "string" ? current : void 0;
1154
1158
  }
1159
+ function mergedLocale(locale) {
1160
+ const stat = translations.get(locale);
1161
+ const auth = authored.get(locale);
1162
+ if (stat && auth) return deepMerge(stat, auth);
1163
+ return auth ?? stat;
1164
+ }
1155
1165
  function resolveTranslations(locale) {
1156
- if (translations.has(locale)) return translations.get(locale);
1157
- const resolved = resolveLocale(locale, [...translations.keys()]);
1158
- if (resolved) return translations.get(resolved);
1166
+ const exact = mergedLocale(locale);
1167
+ if (exact) return exact;
1168
+ const allLocales = [.../* @__PURE__ */ new Set([...translations.keys(), ...authored.keys()])];
1169
+ const resolved = resolveLocale(locale, allLocales);
1170
+ if (resolved) return mergedLocale(resolved);
1159
1171
  return void 0;
1160
1172
  }
1161
1173
  return {
1162
1174
  _fallback: true,
1163
1175
  _serviceName: "i18n",
1164
1176
  t(key, locale, params) {
1165
- const data = resolveTranslations(locale) ?? translations.get(defaultLocale);
1177
+ const data = resolveTranslations(locale) ?? mergedLocale(defaultLocale);
1166
1178
  const value = data ? resolveKey(data, key) : void 0;
1167
1179
  if (value == null) return key;
1168
1180
  if (!params) return value;
@@ -1179,8 +1191,21 @@ function createMemoryI18n() {
1179
1191
  translations.set(locale, { ...data });
1180
1192
  }
1181
1193
  },
1194
+ /**
1195
+ * Replace the ENTIRE runtime-authored translation layer (#2591). Called
1196
+ * by the authored-translation sync with the full current set of active
1197
+ * `translation` metadata items keyed by locale. Wholesale replacement —
1198
+ * not a merge — so deleted items/keys stop resolving on the next sync.
1199
+ */
1200
+ replaceAuthoredTranslations(byLocale) {
1201
+ authored.clear();
1202
+ for (const [locale, data] of Object.entries(byLocale ?? {})) {
1203
+ if (!data || typeof data !== "object") continue;
1204
+ authored.set(locale, { ...data });
1205
+ }
1206
+ },
1182
1207
  getLocales() {
1183
- return [...translations.keys()];
1208
+ return [.../* @__PURE__ */ new Set([...translations.keys(), ...authored.keys()])];
1184
1209
  },
1185
1210
  getDefaultLocale() {
1186
1211
  return defaultLocale;
@@ -1245,6 +1270,114 @@ function createMemoryMetadata() {
1245
1270
  };
1246
1271
  }
1247
1272
 
1273
+ // src/fallbacks/authored-translation-sync.ts
1274
+ var OWNER_PROP = "__authoredTranslationSyncOwner";
1275
+ var LOCALE_LIKE = /^[a-z]{2,3}([_-]([A-Za-z]{4}|[A-Za-z]{2}|[0-9]{3}))?$/;
1276
+ async function readAuthoredTranslationLayer(engine, logger) {
1277
+ let rows;
1278
+ try {
1279
+ rows = await engine.find("sys_metadata", {
1280
+ where: { type: "translation", state: "active" }
1281
+ }) ?? [];
1282
+ if (rows.length === 0) {
1283
+ rows = await engine.find("sys_metadata", {
1284
+ where: { type: "translations", state: "active" }
1285
+ }) ?? [];
1286
+ }
1287
+ } catch (err) {
1288
+ logger?.debug?.("[i18n] authored-translation read failed \u2014 keeping current layer", {
1289
+ error: err?.message
1290
+ });
1291
+ return null;
1292
+ }
1293
+ const byLocale = {};
1294
+ const sorted = [...rows].sort((a, b) => String(a?.name ?? "").localeCompare(String(b?.name ?? "")));
1295
+ for (const row of sorted) {
1296
+ let data;
1297
+ try {
1298
+ data = typeof row.metadata === "string" ? JSON.parse(row.metadata) : row.metadata;
1299
+ } catch {
1300
+ continue;
1301
+ }
1302
+ if (!data || typeof data !== "object") continue;
1303
+ const locale = typeof data?._meta?.locale === "string" && data._meta.locale || typeof data?.locale === "string" && data.locale || (typeof row?.name === "string" && LOCALE_LIKE.test(row.name) ? row.name : void 0) || void 0;
1304
+ if (!locale) {
1305
+ logger?.warn?.(
1306
+ `[i18n] authored translation '${row?.name}' has no resolvable locale (set _meta.locale, or name the item after its BCP-47 locale) \u2014 skipped`
1307
+ );
1308
+ continue;
1309
+ }
1310
+ const { name: _n, locale: _l, _packageId: _p, _provenance: _pr, _lock: _lk, ...payload } = data;
1311
+ byLocale[locale] = deepMerge(byLocale[locale] ?? {}, payload);
1312
+ }
1313
+ return byLocale;
1314
+ }
1315
+ function wireAuthoredTranslationSync(ctx) {
1316
+ if (typeof ctx.hook !== "function") return;
1317
+ const token = /* @__PURE__ */ Symbol("authored-translation-sync");
1318
+ const resolveOwnedI18n = () => {
1319
+ let i18n;
1320
+ try {
1321
+ i18n = ctx.getService("i18n");
1322
+ } catch {
1323
+ return null;
1324
+ }
1325
+ if (!i18n || typeof i18n.replaceAuthoredTranslations !== "function") return null;
1326
+ const current = i18n[OWNER_PROP];
1327
+ if (current === void 0) {
1328
+ i18n[OWNER_PROP] = token;
1329
+ return i18n;
1330
+ }
1331
+ return current === token ? i18n : null;
1332
+ };
1333
+ let chain = Promise.resolve();
1334
+ const sync = () => {
1335
+ const run = chain.then(async () => {
1336
+ const i18n = resolveOwnedI18n();
1337
+ if (!i18n) return;
1338
+ let engine;
1339
+ try {
1340
+ engine = ctx.getService("objectql");
1341
+ } catch {
1342
+ return;
1343
+ }
1344
+ if (!engine || typeof engine.find !== "function") return;
1345
+ const layer = await readAuthoredTranslationLayer(engine, ctx.logger);
1346
+ if (layer === null) return;
1347
+ i18n.replaceAuthoredTranslations(layer);
1348
+ ctx.logger.info?.("[i18n] synced runtime-authored translations", {
1349
+ locales: Object.keys(layer)
1350
+ });
1351
+ });
1352
+ chain = run.catch(() => void 0);
1353
+ return run;
1354
+ };
1355
+ ctx.hook("kernel:ready", async () => {
1356
+ if (resolveOwnedI18n()) {
1357
+ let protocol = null;
1358
+ try {
1359
+ protocol = ctx.getService("protocol");
1360
+ } catch {
1361
+ }
1362
+ if (protocol && typeof protocol.onMetadataMutation === "function") {
1363
+ protocol.onMetadataMutation((evt) => {
1364
+ if (evt?.type !== "translation" || evt.state === "draft") return;
1365
+ void sync().catch((err) => {
1366
+ ctx.logger.warn?.("[i18n] authored-translation re-sync after mutation failed", {
1367
+ item: evt.name,
1368
+ error: err?.message
1369
+ });
1370
+ });
1371
+ });
1372
+ }
1373
+ }
1374
+ await sync();
1375
+ });
1376
+ ctx.hook("metadata:reloaded", async () => {
1377
+ await sync();
1378
+ });
1379
+ }
1380
+
1248
1381
  // src/fallbacks/index.ts
1249
1382
  var CORE_FALLBACK_FACTORIES = {
1250
1383
  metadata: createMemoryMetadata,
@@ -5281,6 +5414,7 @@ var NamespaceResolver = class {
5281
5414
  createMemoryQueue,
5282
5415
  createPluginConfigValidator,
5283
5416
  createPluginPermissionEnforcer,
5417
+ deepMerge,
5284
5418
  evaluateAuthGate,
5285
5419
  extractApiKey,
5286
5420
  generateApiKey,
@@ -5293,6 +5427,7 @@ var NamespaceResolver = class {
5293
5427
  isNode,
5294
5428
  parseScopes,
5295
5429
  parseSignature,
5430
+ readAuthoredTranslationLayer,
5296
5431
  resolveApiKeyPrincipal,
5297
5432
  resolveAuthzContext,
5298
5433
  resolveLocale,
@@ -5302,6 +5437,7 @@ var NamespaceResolver = class {
5302
5437
  verifyPayload,
5303
5438
  verifyPlatformSignature,
5304
5439
  verifyPluginArtifact,
5305
- verifyPublisherSignature
5440
+ verifyPublisherSignature,
5441
+ wireAuthoredTranslationSync
5306
5442
  });
5307
5443
  //# sourceMappingURL=index.cjs.map