@noy-db/hub 0.1.0-pre.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/LICENSE +21 -0
- package/README.md +197 -0
- package/dist/aggregate/index.cjs +476 -0
- package/dist/aggregate/index.cjs.map +1 -0
- package/dist/aggregate/index.d.cts +38 -0
- package/dist/aggregate/index.d.ts +38 -0
- package/dist/aggregate/index.js +53 -0
- package/dist/aggregate/index.js.map +1 -0
- package/dist/blobs/index.cjs +1480 -0
- package/dist/blobs/index.cjs.map +1 -0
- package/dist/blobs/index.d.cts +45 -0
- package/dist/blobs/index.d.ts +45 -0
- package/dist/blobs/index.js +48 -0
- package/dist/blobs/index.js.map +1 -0
- package/dist/bundle/index.cjs +436 -0
- package/dist/bundle/index.cjs.map +1 -0
- package/dist/bundle/index.d.cts +7 -0
- package/dist/bundle/index.d.ts +7 -0
- package/dist/bundle/index.js +40 -0
- package/dist/bundle/index.js.map +1 -0
- package/dist/chunk-2QR2PQTT.js +217 -0
- package/dist/chunk-2QR2PQTT.js.map +1 -0
- package/dist/chunk-4OWFYIDQ.js +79 -0
- package/dist/chunk-4OWFYIDQ.js.map +1 -0
- package/dist/chunk-5AATM2M2.js +90 -0
- package/dist/chunk-5AATM2M2.js.map +1 -0
- package/dist/chunk-ACLDOTNQ.js +543 -0
- package/dist/chunk-ACLDOTNQ.js.map +1 -0
- package/dist/chunk-BTDCBVJW.js +160 -0
- package/dist/chunk-BTDCBVJW.js.map +1 -0
- package/dist/chunk-CIMZBAZB.js +72 -0
- package/dist/chunk-CIMZBAZB.js.map +1 -0
- package/dist/chunk-E445ICYI.js +365 -0
- package/dist/chunk-E445ICYI.js.map +1 -0
- package/dist/chunk-EXQRC2L4.js +722 -0
- package/dist/chunk-EXQRC2L4.js.map +1 -0
- package/dist/chunk-FZU343FL.js +32 -0
- package/dist/chunk-FZU343FL.js.map +1 -0
- package/dist/chunk-GJILMRPO.js +354 -0
- package/dist/chunk-GJILMRPO.js.map +1 -0
- package/dist/chunk-GOUT6DND.js +1285 -0
- package/dist/chunk-GOUT6DND.js.map +1 -0
- package/dist/chunk-J66GRPNH.js +111 -0
- package/dist/chunk-J66GRPNH.js.map +1 -0
- package/dist/chunk-M2F2JAWB.js +464 -0
- package/dist/chunk-M2F2JAWB.js.map +1 -0
- package/dist/chunk-M5INGEFC.js +84 -0
- package/dist/chunk-M5INGEFC.js.map +1 -0
- package/dist/chunk-M62XNWRA.js +72 -0
- package/dist/chunk-M62XNWRA.js.map +1 -0
- package/dist/chunk-MR4424N3.js +275 -0
- package/dist/chunk-MR4424N3.js.map +1 -0
- package/dist/chunk-NPC4LFV5.js +132 -0
- package/dist/chunk-NPC4LFV5.js.map +1 -0
- package/dist/chunk-NXFEYLVG.js +311 -0
- package/dist/chunk-NXFEYLVG.js.map +1 -0
- package/dist/chunk-R36SIKES.js +79 -0
- package/dist/chunk-R36SIKES.js.map +1 -0
- package/dist/chunk-TDR6T5CJ.js +381 -0
- package/dist/chunk-TDR6T5CJ.js.map +1 -0
- package/dist/chunk-UF3BUNQZ.js +1 -0
- package/dist/chunk-UF3BUNQZ.js.map +1 -0
- package/dist/chunk-UQFSPSWG.js +1109 -0
- package/dist/chunk-UQFSPSWG.js.map +1 -0
- package/dist/chunk-USKYUS74.js +793 -0
- package/dist/chunk-USKYUS74.js.map +1 -0
- package/dist/chunk-XCL3WP6J.js +121 -0
- package/dist/chunk-XCL3WP6J.js.map +1 -0
- package/dist/chunk-XHFOENR2.js +680 -0
- package/dist/chunk-XHFOENR2.js.map +1 -0
- package/dist/chunk-ZFKD4QMV.js +430 -0
- package/dist/chunk-ZFKD4QMV.js.map +1 -0
- package/dist/chunk-ZLMV3TUA.js +490 -0
- package/dist/chunk-ZLMV3TUA.js.map +1 -0
- package/dist/chunk-ZRG4V3F5.js +17 -0
- package/dist/chunk-ZRG4V3F5.js.map +1 -0
- package/dist/consent/index.cjs +204 -0
- package/dist/consent/index.cjs.map +1 -0
- package/dist/consent/index.d.cts +24 -0
- package/dist/consent/index.d.ts +24 -0
- package/dist/consent/index.js +23 -0
- package/dist/consent/index.js.map +1 -0
- package/dist/crdt/index.cjs +152 -0
- package/dist/crdt/index.cjs.map +1 -0
- package/dist/crdt/index.d.cts +30 -0
- package/dist/crdt/index.d.ts +30 -0
- package/dist/crdt/index.js +24 -0
- package/dist/crdt/index.js.map +1 -0
- package/dist/crypto-IVKU7YTT.js +44 -0
- package/dist/crypto-IVKU7YTT.js.map +1 -0
- package/dist/delegation-XDJCBTI2.js +16 -0
- package/dist/delegation-XDJCBTI2.js.map +1 -0
- package/dist/dev-unlock-CeXic1xC.d.cts +263 -0
- package/dist/dev-unlock-KrKkcqD3.d.ts +263 -0
- package/dist/hash-9KO1BGxh.d.cts +63 -0
- package/dist/hash-ChfJjRjQ.d.ts +63 -0
- package/dist/history/index.cjs +1215 -0
- package/dist/history/index.cjs.map +1 -0
- package/dist/history/index.d.cts +62 -0
- package/dist/history/index.d.ts +62 -0
- package/dist/history/index.js +79 -0
- package/dist/history/index.js.map +1 -0
- package/dist/i18n/index.cjs +746 -0
- package/dist/i18n/index.cjs.map +1 -0
- package/dist/i18n/index.d.cts +38 -0
- package/dist/i18n/index.d.ts +38 -0
- package/dist/i18n/index.js +55 -0
- package/dist/i18n/index.js.map +1 -0
- package/dist/index-BRHBCmLt.d.ts +1940 -0
- package/dist/index-C8kQtmOk.d.ts +380 -0
- package/dist/index-DN-J-5wT.d.cts +1940 -0
- package/dist/index-DhjMjz7L.d.cts +380 -0
- package/dist/index.cjs +14756 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +269 -0
- package/dist/index.d.ts +269 -0
- package/dist/index.js +6085 -0
- package/dist/index.js.map +1 -0
- package/dist/indexing/index.cjs +736 -0
- package/dist/indexing/index.cjs.map +1 -0
- package/dist/indexing/index.d.cts +36 -0
- package/dist/indexing/index.d.ts +36 -0
- package/dist/indexing/index.js +77 -0
- package/dist/indexing/index.js.map +1 -0
- package/dist/lazy-builder-BwEoBQZ9.d.ts +304 -0
- package/dist/lazy-builder-CZVLKh0Z.d.cts +304 -0
- package/dist/ledger-2NX4L7PN.js +33 -0
- package/dist/ledger-2NX4L7PN.js.map +1 -0
- package/dist/mime-magic-CBBSOkjm.d.cts +50 -0
- package/dist/mime-magic-CBBSOkjm.d.ts +50 -0
- package/dist/periods/index.cjs +1035 -0
- package/dist/periods/index.cjs.map +1 -0
- package/dist/periods/index.d.cts +21 -0
- package/dist/periods/index.d.ts +21 -0
- package/dist/periods/index.js +25 -0
- package/dist/periods/index.js.map +1 -0
- package/dist/predicate-SBHmi6D0.d.cts +161 -0
- package/dist/predicate-SBHmi6D0.d.ts +161 -0
- package/dist/query/index.cjs +1957 -0
- package/dist/query/index.cjs.map +1 -0
- package/dist/query/index.d.cts +3 -0
- package/dist/query/index.d.ts +3 -0
- package/dist/query/index.js +62 -0
- package/dist/query/index.js.map +1 -0
- package/dist/session/index.cjs +487 -0
- package/dist/session/index.cjs.map +1 -0
- package/dist/session/index.d.cts +45 -0
- package/dist/session/index.d.ts +45 -0
- package/dist/session/index.js +44 -0
- package/dist/session/index.js.map +1 -0
- package/dist/shadow/index.cjs +133 -0
- package/dist/shadow/index.cjs.map +1 -0
- package/dist/shadow/index.d.cts +16 -0
- package/dist/shadow/index.d.ts +16 -0
- package/dist/shadow/index.js +20 -0
- package/dist/shadow/index.js.map +1 -0
- package/dist/store/index.cjs +1069 -0
- package/dist/store/index.cjs.map +1 -0
- package/dist/store/index.d.cts +491 -0
- package/dist/store/index.d.ts +491 -0
- package/dist/store/index.js +34 -0
- package/dist/store/index.js.map +1 -0
- package/dist/strategy-BSxFXGzb.d.cts +110 -0
- package/dist/strategy-BSxFXGzb.d.ts +110 -0
- package/dist/strategy-D-SrOLCl.d.cts +548 -0
- package/dist/strategy-D-SrOLCl.d.ts +548 -0
- package/dist/sync/index.cjs +1062 -0
- package/dist/sync/index.cjs.map +1 -0
- package/dist/sync/index.d.cts +42 -0
- package/dist/sync/index.d.ts +42 -0
- package/dist/sync/index.js +28 -0
- package/dist/sync/index.js.map +1 -0
- package/dist/team/index.cjs +1233 -0
- package/dist/team/index.cjs.map +1 -0
- package/dist/team/index.d.cts +117 -0
- package/dist/team/index.d.ts +117 -0
- package/dist/team/index.js +39 -0
- package/dist/team/index.js.map +1 -0
- package/dist/tx/index.cjs +212 -0
- package/dist/tx/index.cjs.map +1 -0
- package/dist/tx/index.d.cts +20 -0
- package/dist/tx/index.d.ts +20 -0
- package/dist/tx/index.js +20 -0
- package/dist/tx/index.js.map +1 -0
- package/dist/types-BZpCZB8N.d.ts +7526 -0
- package/dist/types-Bfs0qr5F.d.cts +7526 -0
- package/dist/ulid-COREQ2RQ.js +9 -0
- package/dist/ulid-COREQ2RQ.js.map +1 -0
- package/dist/util/index.cjs +230 -0
- package/dist/util/index.cjs.map +1 -0
- package/dist/util/index.d.cts +77 -0
- package/dist/util/index.d.ts +77 -0
- package/dist/util/index.js +190 -0
- package/dist/util/index.js.map +1 -0
- package/package.json +244 -0
|
@@ -0,0 +1,490 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ensureCollectionDEK
|
|
3
|
+
} from "./chunk-M2F2JAWB.js";
|
|
4
|
+
import {
|
|
5
|
+
envelopePayloadHash
|
|
6
|
+
} from "./chunk-CIMZBAZB.js";
|
|
7
|
+
import {
|
|
8
|
+
NOYDB_FORMAT_VERSION
|
|
9
|
+
} from "./chunk-ZRG4V3F5.js";
|
|
10
|
+
import {
|
|
11
|
+
decrypt,
|
|
12
|
+
encrypt
|
|
13
|
+
} from "./chunk-MR4424N3.js";
|
|
14
|
+
import {
|
|
15
|
+
DictKeyMissingError,
|
|
16
|
+
LocaleNotSpecifiedError,
|
|
17
|
+
MissingTranslationError,
|
|
18
|
+
PermissionDeniedError
|
|
19
|
+
} from "./chunk-ACLDOTNQ.js";
|
|
20
|
+
|
|
21
|
+
// src/i18n/dictionary.ts
|
|
22
|
+
var DICT_COLLECTION_PREFIX = "_dict_";
|
|
23
|
+
function dictCollectionName(dictionaryName) {
|
|
24
|
+
return `${DICT_COLLECTION_PREFIX}${dictionaryName}`;
|
|
25
|
+
}
|
|
26
|
+
function isDictCollectionName(name) {
|
|
27
|
+
return name.startsWith(DICT_COLLECTION_PREFIX);
|
|
28
|
+
}
|
|
29
|
+
function dictKey(name, keys) {
|
|
30
|
+
return { _noydbDictKey: true, name, keys };
|
|
31
|
+
}
|
|
32
|
+
function isDictKeyDescriptor(x) {
|
|
33
|
+
return typeof x === "object" && x !== null && x._noydbDictKey === true;
|
|
34
|
+
}
|
|
35
|
+
var DictionaryHandle = class {
|
|
36
|
+
constructor(adapter, compartmentName, dictionaryName, keyring, getDEK, encrypted, ledger, options, findAndUpdateReferences, emitter) {
|
|
37
|
+
this.adapter = adapter;
|
|
38
|
+
this.compartmentName = compartmentName;
|
|
39
|
+
this.dictionaryName = dictionaryName;
|
|
40
|
+
this.keyring = keyring;
|
|
41
|
+
this.getDEK = getDEK;
|
|
42
|
+
this.encrypted = encrypted;
|
|
43
|
+
this.ledger = ledger;
|
|
44
|
+
this.options = options;
|
|
45
|
+
this.findAndUpdateReferences = findAndUpdateReferences;
|
|
46
|
+
this.emitter = emitter;
|
|
47
|
+
this.collName = dictCollectionName(dictionaryName);
|
|
48
|
+
}
|
|
49
|
+
adapter;
|
|
50
|
+
compartmentName;
|
|
51
|
+
dictionaryName;
|
|
52
|
+
keyring;
|
|
53
|
+
getDEK;
|
|
54
|
+
encrypted;
|
|
55
|
+
ledger;
|
|
56
|
+
options;
|
|
57
|
+
findAndUpdateReferences;
|
|
58
|
+
emitter;
|
|
59
|
+
collName;
|
|
60
|
+
/**
|
|
61
|
+
* Synchronous write-through cache for dict-join support.
|
|
62
|
+
* Populated on every `put()`, `delete()`, and `rename()`. The snapshot
|
|
63
|
+
* is built from this cache by `snapshotEntries()` — the query executor
|
|
64
|
+
* calls this synchronously inside `.toArray()`.
|
|
65
|
+
*
|
|
66
|
+
* `null` means "not yet initialized" — callers should use `list()`
|
|
67
|
+
* to warm the cache before using dict joins on pre-existing data.
|
|
68
|
+
*/
|
|
69
|
+
_syncCache = /* @__PURE__ */ new Map();
|
|
70
|
+
/**
|
|
71
|
+
* Return all cached entries as `{ key, labels, ...labels }` records —
|
|
72
|
+
* usable synchronously by the join executor's `snapshot()` call.
|
|
73
|
+
* Returns an empty array when the cache has never been populated.
|
|
74
|
+
*/
|
|
75
|
+
snapshotEntries() {
|
|
76
|
+
return Array.from(this._syncCache.values()).map((e) => ({
|
|
77
|
+
key: e.key,
|
|
78
|
+
labels: e.labels,
|
|
79
|
+
...e.labels
|
|
80
|
+
}));
|
|
81
|
+
}
|
|
82
|
+
// ─── Access checks ────────────────────────────────────────────────
|
|
83
|
+
requireWriteAccess() {
|
|
84
|
+
const minRole = this.options.writableBy ?? "admin";
|
|
85
|
+
const roleRank = {
|
|
86
|
+
client: 1,
|
|
87
|
+
viewer: 2,
|
|
88
|
+
operator: 3,
|
|
89
|
+
admin: 4,
|
|
90
|
+
owner: 5
|
|
91
|
+
};
|
|
92
|
+
const callerRank = roleRank[this.keyring.role] ?? 0;
|
|
93
|
+
const requiredRank = roleRank[minRole] ?? 4;
|
|
94
|
+
if (callerRank < requiredRank) {
|
|
95
|
+
throw new PermissionDeniedError(
|
|
96
|
+
`Dictionary "${this.dictionaryName}" writes require "${minRole}" role or above. Current role: "${this.keyring.role}".`
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
// ─── Internal helpers ─────────────────────────────────────────────
|
|
101
|
+
async getDekForDict() {
|
|
102
|
+
const resolve = await ensureCollectionDEK(
|
|
103
|
+
this.adapter,
|
|
104
|
+
this.compartmentName,
|
|
105
|
+
this.keyring
|
|
106
|
+
);
|
|
107
|
+
return resolve(this.collName);
|
|
108
|
+
}
|
|
109
|
+
async encryptEntry(entry, version) {
|
|
110
|
+
if (!this.encrypted) {
|
|
111
|
+
return {
|
|
112
|
+
_noydb: NOYDB_FORMAT_VERSION,
|
|
113
|
+
_v: version,
|
|
114
|
+
_ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
115
|
+
_iv: "",
|
|
116
|
+
_data: JSON.stringify(entry),
|
|
117
|
+
_by: this.keyring.userId
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
const dek = await this.getDekForDict();
|
|
121
|
+
const { iv, data } = await encrypt(JSON.stringify(entry), dek);
|
|
122
|
+
return {
|
|
123
|
+
_noydb: NOYDB_FORMAT_VERSION,
|
|
124
|
+
_v: version,
|
|
125
|
+
_ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
126
|
+
_iv: iv,
|
|
127
|
+
_data: data,
|
|
128
|
+
_by: this.keyring.userId
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
async decryptEntry(envelope) {
|
|
132
|
+
if (!this.encrypted) {
|
|
133
|
+
return JSON.parse(envelope._data);
|
|
134
|
+
}
|
|
135
|
+
const dek = await this.getDekForDict();
|
|
136
|
+
const json = await decrypt(envelope._iv, envelope._data, dek);
|
|
137
|
+
return JSON.parse(json);
|
|
138
|
+
}
|
|
139
|
+
// ─── Public API ───────────────────────────────────────────────────
|
|
140
|
+
/**
|
|
141
|
+
* Add or overwrite a single dictionary entry.
|
|
142
|
+
*
|
|
143
|
+
* @param key The stable key to store (e.g. `'paid'`).
|
|
144
|
+
* @param labels Locale → label map (e.g. `{ en: 'Paid', th: 'ชำระแล้ว' }`).
|
|
145
|
+
*/
|
|
146
|
+
async put(key, labels) {
|
|
147
|
+
this.requireWriteAccess();
|
|
148
|
+
const entry = { key, labels };
|
|
149
|
+
const existing = await this.adapter.get(
|
|
150
|
+
this.compartmentName,
|
|
151
|
+
this.collName,
|
|
152
|
+
key
|
|
153
|
+
);
|
|
154
|
+
const version = existing ? existing._v + 1 : 1;
|
|
155
|
+
const envelope = await this.encryptEntry(entry, version);
|
|
156
|
+
await this.adapter.put(
|
|
157
|
+
this.compartmentName,
|
|
158
|
+
this.collName,
|
|
159
|
+
key,
|
|
160
|
+
envelope,
|
|
161
|
+
existing ? existing._v : void 0
|
|
162
|
+
);
|
|
163
|
+
this._syncCache.set(key, entry);
|
|
164
|
+
this.emitter.emit("change", {
|
|
165
|
+
vault: this.compartmentName,
|
|
166
|
+
collection: this.collName,
|
|
167
|
+
id: key,
|
|
168
|
+
action: "put"
|
|
169
|
+
});
|
|
170
|
+
if (this.ledger) {
|
|
171
|
+
await this.ledger.append({
|
|
172
|
+
op: "put",
|
|
173
|
+
collection: this.collName,
|
|
174
|
+
id: key,
|
|
175
|
+
version,
|
|
176
|
+
actor: this.keyring.userId,
|
|
177
|
+
// — must be the real envelope hash so
|
|
178
|
+
// vault.verifyBackupIntegrity()'s data-cross-check matches.
|
|
179
|
+
payloadHash: await envelopePayloadHash(envelope)
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Batch-add or overwrite multiple dictionary entries in one call.
|
|
185
|
+
*
|
|
186
|
+
* @param entries `{ key: { locale: label } }` map.
|
|
187
|
+
*/
|
|
188
|
+
async putAll(entries) {
|
|
189
|
+
this.requireWriteAccess();
|
|
190
|
+
for (const [key, labels] of Object.entries(entries)) {
|
|
191
|
+
await this.put(key, labels);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Load the label map for a single key.
|
|
196
|
+
*
|
|
197
|
+
* @returns The label map, or `null` if the key doesn't exist.
|
|
198
|
+
*/
|
|
199
|
+
async get(key) {
|
|
200
|
+
const envelope = await this.adapter.get(
|
|
201
|
+
this.compartmentName,
|
|
202
|
+
this.collName,
|
|
203
|
+
key
|
|
204
|
+
);
|
|
205
|
+
if (!envelope) return null;
|
|
206
|
+
const entry = await this.decryptEntry(envelope);
|
|
207
|
+
return entry.labels;
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Delete a dictionary key.
|
|
211
|
+
*
|
|
212
|
+
* Default mode is `'strict'` — throws `DictKeyInUseError` if any
|
|
213
|
+
* registered collection has a record referencing this key. Pass
|
|
214
|
+
* `{ mode: 'warn' }` to skip the check (dev-mode cleanup only).
|
|
215
|
+
*/
|
|
216
|
+
async delete(key, opts = {}) {
|
|
217
|
+
this.requireWriteAccess();
|
|
218
|
+
const existing = await this.adapter.get(
|
|
219
|
+
this.compartmentName,
|
|
220
|
+
this.collName,
|
|
221
|
+
key
|
|
222
|
+
);
|
|
223
|
+
if (!existing) {
|
|
224
|
+
throw new DictKeyMissingError(this.dictionaryName, key);
|
|
225
|
+
}
|
|
226
|
+
const mode = opts.mode ?? "strict";
|
|
227
|
+
if (mode === "strict" && this.findAndUpdateReferences) {
|
|
228
|
+
}
|
|
229
|
+
await this.adapter.delete(this.compartmentName, this.collName, key);
|
|
230
|
+
this._syncCache.delete(key);
|
|
231
|
+
this.emitter.emit("change", {
|
|
232
|
+
vault: this.compartmentName,
|
|
233
|
+
collection: this.collName,
|
|
234
|
+
id: key,
|
|
235
|
+
action: "delete"
|
|
236
|
+
});
|
|
237
|
+
if (this.ledger) {
|
|
238
|
+
await this.ledger.append({
|
|
239
|
+
op: "delete",
|
|
240
|
+
collection: this.collName,
|
|
241
|
+
id: key,
|
|
242
|
+
version: existing._v,
|
|
243
|
+
actor: this.keyring.userId,
|
|
244
|
+
// — for delete the prior envelope is what was just
|
|
245
|
+
// removed; we hash it so the chain captures intent. The
|
|
246
|
+
// verifyBackupIntegrity data-cross-check skips delete
|
|
247
|
+
// entries entirely (the live record is gone), but the
|
|
248
|
+
// chain still benefits from a stable non-empty hash.
|
|
249
|
+
payloadHash: await envelopePayloadHash(existing)
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* Rename a dictionary key — the only sanctioned mass-mutation path.
|
|
255
|
+
*
|
|
256
|
+
* Atomically:
|
|
257
|
+
* 1. Adds the new key with the same labels as the old key.
|
|
258
|
+
* 2. Updates every registered record that stores the old key to
|
|
259
|
+
* store the new key instead.
|
|
260
|
+
* 3. Deletes the old key.
|
|
261
|
+
* 4. Appends a single ledger entry recording the rename.
|
|
262
|
+
*
|
|
263
|
+
* Respects ACL: throws `PermissionDeniedError` before any mutation
|
|
264
|
+
* if the caller can't write. The cascade is best-effort atomic
|
|
265
|
+
* within this call — no two-phase commit across adapter calls.
|
|
266
|
+
*
|
|
267
|
+
* Cascade-on-delete is NOT supported. Use `rename()` when you need
|
|
268
|
+
* to change a key that records reference.
|
|
269
|
+
*/
|
|
270
|
+
async rename(oldKey, newKey) {
|
|
271
|
+
this.requireWriteAccess();
|
|
272
|
+
const existing = await this.adapter.get(
|
|
273
|
+
this.compartmentName,
|
|
274
|
+
this.collName,
|
|
275
|
+
oldKey
|
|
276
|
+
);
|
|
277
|
+
if (!existing) {
|
|
278
|
+
throw new DictKeyMissingError(this.dictionaryName, oldKey);
|
|
279
|
+
}
|
|
280
|
+
const oldEntry = await this.decryptEntry(existing);
|
|
281
|
+
const newEntry = { key: newKey, labels: oldEntry.labels };
|
|
282
|
+
const newEnvelope = await this.encryptEntry(newEntry, 1);
|
|
283
|
+
await this.adapter.put(
|
|
284
|
+
this.compartmentName,
|
|
285
|
+
this.collName,
|
|
286
|
+
newKey,
|
|
287
|
+
newEnvelope
|
|
288
|
+
);
|
|
289
|
+
if (this.findAndUpdateReferences) {
|
|
290
|
+
await this.findAndUpdateReferences(this.dictionaryName, oldKey, newKey);
|
|
291
|
+
}
|
|
292
|
+
await this.adapter.delete(this.compartmentName, this.collName, oldKey);
|
|
293
|
+
this._syncCache.delete(oldKey);
|
|
294
|
+
this._syncCache.set(newKey, newEntry);
|
|
295
|
+
this.emitter.emit("change", {
|
|
296
|
+
vault: this.compartmentName,
|
|
297
|
+
collection: this.collName,
|
|
298
|
+
id: oldKey,
|
|
299
|
+
action: "delete"
|
|
300
|
+
});
|
|
301
|
+
this.emitter.emit("change", {
|
|
302
|
+
vault: this.compartmentName,
|
|
303
|
+
collection: this.collName,
|
|
304
|
+
id: newKey,
|
|
305
|
+
action: "put"
|
|
306
|
+
});
|
|
307
|
+
if (this.ledger) {
|
|
308
|
+
await this.ledger.append({
|
|
309
|
+
op: "delete",
|
|
310
|
+
collection: this.collName,
|
|
311
|
+
id: oldKey,
|
|
312
|
+
version: existing._v,
|
|
313
|
+
actor: this.keyring.userId,
|
|
314
|
+
payloadHash: await envelopePayloadHash(existing)
|
|
315
|
+
});
|
|
316
|
+
await this.ledger.append({
|
|
317
|
+
op: "put",
|
|
318
|
+
collection: this.collName,
|
|
319
|
+
id: newKey,
|
|
320
|
+
version: 1,
|
|
321
|
+
actor: this.keyring.userId,
|
|
322
|
+
payloadHash: await envelopePayloadHash(newEnvelope)
|
|
323
|
+
});
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
/**
|
|
327
|
+
* List all entries in this dictionary.
|
|
328
|
+
*
|
|
329
|
+
* @returns Array of `{ key, labels }` objects.
|
|
330
|
+
*/
|
|
331
|
+
async list() {
|
|
332
|
+
const keys = await this.adapter.list(this.compartmentName, this.collName);
|
|
333
|
+
const entries = [];
|
|
334
|
+
for (const key of keys) {
|
|
335
|
+
const envelope = await this.adapter.get(
|
|
336
|
+
this.compartmentName,
|
|
337
|
+
this.collName,
|
|
338
|
+
key
|
|
339
|
+
);
|
|
340
|
+
if (!envelope) continue;
|
|
341
|
+
const entry = await this.decryptEntry(envelope);
|
|
342
|
+
entries.push(entry);
|
|
343
|
+
this._syncCache.set(key, entry);
|
|
344
|
+
}
|
|
345
|
+
return entries;
|
|
346
|
+
}
|
|
347
|
+
/**
|
|
348
|
+
* Resolve a key to its label for the given locale.
|
|
349
|
+
*
|
|
350
|
+
* Used by the collection's locale-aware read path to populate
|
|
351
|
+
* `<field>Label` virtual fields. Returns `undefined` when the
|
|
352
|
+
* key doesn't exist or has no label for the requested locale
|
|
353
|
+
* (after exhausting the fallback chain).
|
|
354
|
+
*/
|
|
355
|
+
async resolveLabel(key, locale, fallback) {
|
|
356
|
+
const labels = await this.get(key);
|
|
357
|
+
if (!labels) return void 0;
|
|
358
|
+
if (labels[locale] !== void 0) return labels[locale];
|
|
359
|
+
const chain = Array.isArray(fallback) ? fallback : fallback ? [fallback] : [];
|
|
360
|
+
for (const fb of chain) {
|
|
361
|
+
if (fb === "any") {
|
|
362
|
+
const any = Object.values(labels)[0];
|
|
363
|
+
if (any !== void 0) return any;
|
|
364
|
+
} else if (labels[fb] !== void 0) {
|
|
365
|
+
return labels[fb];
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
return void 0;
|
|
369
|
+
}
|
|
370
|
+
};
|
|
371
|
+
|
|
372
|
+
// src/i18n/core.ts
|
|
373
|
+
function i18nText(options) {
|
|
374
|
+
return { _noydbI18nText: true, options };
|
|
375
|
+
}
|
|
376
|
+
function isI18nTextDescriptor(x) {
|
|
377
|
+
return typeof x === "object" && x !== null && x._noydbI18nText === true;
|
|
378
|
+
}
|
|
379
|
+
function validateI18nTextValue(value, field, descriptor) {
|
|
380
|
+
const { options } = descriptor;
|
|
381
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
382
|
+
throw new MissingTranslationError(
|
|
383
|
+
field,
|
|
384
|
+
options.languages,
|
|
385
|
+
`Field "${field}" must be a { [locale]: string } map, got ${typeof value}.`
|
|
386
|
+
);
|
|
387
|
+
}
|
|
388
|
+
const map = value;
|
|
389
|
+
for (const [locale, v] of Object.entries(map)) {
|
|
390
|
+
if (typeof v !== "string") {
|
|
391
|
+
throw new MissingTranslationError(
|
|
392
|
+
field,
|
|
393
|
+
[locale],
|
|
394
|
+
`Field "${field}": locale "${locale}" must be a string, got ${typeof v}.`
|
|
395
|
+
);
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
const { required } = options;
|
|
399
|
+
if (required === "all") {
|
|
400
|
+
const missing = options.languages.filter(
|
|
401
|
+
(lang) => !(lang in map) || map[lang] === ""
|
|
402
|
+
);
|
|
403
|
+
if (missing.length > 0) {
|
|
404
|
+
throw new MissingTranslationError(
|
|
405
|
+
field,
|
|
406
|
+
missing,
|
|
407
|
+
`Field "${field}" requires all declared languages. Missing: ${missing.join(", ")}.`
|
|
408
|
+
);
|
|
409
|
+
}
|
|
410
|
+
} else if (required === "any") {
|
|
411
|
+
const present = options.languages.some(
|
|
412
|
+
(lang) => lang in map && map[lang] !== ""
|
|
413
|
+
);
|
|
414
|
+
if (!present) {
|
|
415
|
+
throw new MissingTranslationError(
|
|
416
|
+
field,
|
|
417
|
+
options.languages,
|
|
418
|
+
`Field "${field}" requires at least one declared language. None present.`
|
|
419
|
+
);
|
|
420
|
+
}
|
|
421
|
+
} else {
|
|
422
|
+
const requiredList = required;
|
|
423
|
+
const missing = requiredList.filter(
|
|
424
|
+
(lang) => !(lang in map) || map[lang] === ""
|
|
425
|
+
);
|
|
426
|
+
if (missing.length > 0) {
|
|
427
|
+
throw new MissingTranslationError(
|
|
428
|
+
field,
|
|
429
|
+
missing,
|
|
430
|
+
`Field "${field}" requires: ${requiredList.join(", ")}. Missing: ${missing.join(", ")}.`
|
|
431
|
+
);
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
function resolveI18nText(value, locale, fallback, field) {
|
|
436
|
+
if (locale === "raw") {
|
|
437
|
+
return value;
|
|
438
|
+
}
|
|
439
|
+
if (!locale) {
|
|
440
|
+
throw new LocaleNotSpecifiedError(field ?? "<unknown>");
|
|
441
|
+
}
|
|
442
|
+
if (value[locale] !== void 0 && value[locale] !== "") {
|
|
443
|
+
return value[locale];
|
|
444
|
+
}
|
|
445
|
+
const chain = Array.isArray(fallback) ? fallback : fallback ? [fallback] : [];
|
|
446
|
+
for (const fb of chain) {
|
|
447
|
+
if (fb === "any") {
|
|
448
|
+
const any = Object.values(value).find((v) => v !== "");
|
|
449
|
+
if (any !== void 0) return any;
|
|
450
|
+
} else if (value[fb] !== void 0 && value[fb] !== "") {
|
|
451
|
+
return value[fb];
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
throw new LocaleNotSpecifiedError(
|
|
455
|
+
field ?? "<unknown>",
|
|
456
|
+
`No translation available for locale "${locale}"` + (chain.length > 0 ? ` or fallback chain [${chain.join(", ")}]` : "") + "."
|
|
457
|
+
);
|
|
458
|
+
}
|
|
459
|
+
function applyI18nLocale(record, i18nFields, locale, fallback) {
|
|
460
|
+
const fieldNames = Object.keys(i18nFields);
|
|
461
|
+
if (fieldNames.length === 0) return record;
|
|
462
|
+
const result = { ...record };
|
|
463
|
+
for (const field of fieldNames) {
|
|
464
|
+
const raw = result[field];
|
|
465
|
+
if (raw === void 0 || raw === null) continue;
|
|
466
|
+
if (typeof raw !== "object" || Array.isArray(raw)) continue;
|
|
467
|
+
result[field] = resolveI18nText(
|
|
468
|
+
raw,
|
|
469
|
+
locale,
|
|
470
|
+
fallback,
|
|
471
|
+
field
|
|
472
|
+
);
|
|
473
|
+
}
|
|
474
|
+
return result;
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
export {
|
|
478
|
+
DICT_COLLECTION_PREFIX,
|
|
479
|
+
dictCollectionName,
|
|
480
|
+
isDictCollectionName,
|
|
481
|
+
dictKey,
|
|
482
|
+
isDictKeyDescriptor,
|
|
483
|
+
DictionaryHandle,
|
|
484
|
+
i18nText,
|
|
485
|
+
isI18nTextDescriptor,
|
|
486
|
+
validateI18nTextValue,
|
|
487
|
+
resolveI18nText,
|
|
488
|
+
applyI18nLocale
|
|
489
|
+
};
|
|
490
|
+
//# sourceMappingURL=chunk-ZLMV3TUA.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/i18n/dictionary.ts","../src/i18n/core.ts"],"sourcesContent":["/**\n * _dict_* reserved collections + dictKey schema descriptor —\n *\n * Stores bounded enum-like field dictionaries as reserved encrypted\n * collections (`_dict_<name>/`) within a vault. Each dictionary\n * entry maps a stable key (e.g. `'paid'`) to a locale → label record\n * (e.g. `{ en: 'Paid', th: 'ชำระแล้ว' }`).\n *\n * Design decisions\n * ────────────────\n *\n * **Why reserved collections, not a separate store?**\n * Same answer as `_sync_credentials`: the compartment's existing\n * encryption stack is exactly right. Dictionaries are encrypted under the\n * same vault DEK, inherit ACL, ledger, and backup/restore for free.\n *\n * **One collection per dictionary, not one collection with namespaces.**\n * Each `_dict_<name>/` collection holds entries `{ id: key, labels: {...} }`.\n * This composes with `ref()` naturally (a dictKey IS a ref to the dict\n * collection), and means the query DSL works over dictionary entries\n * without any special-casing.\n *\n * **dictKey() is a descriptor, not a Zod type.**\n * The descriptor pattern matches `ref()`: declare NOYDB-specific metadata\n * in the collection options alongside `refs`. TypeScript inference comes\n * from the descriptor's generic parameter, not from Zod internals.\n *\n * API:\n * `dictKey(name, keys?)` — returns a DictKeyDescriptor\n * `vault.dictionary(name)` — returns a DictionaryHandle\n * `DictionaryHandle.put/putAll/get/delete/rename/list` — CRUD\n */\n\nimport type { NoydbStore, EncryptedEnvelope } from '../types.js'\nimport type { NoydbEventEmitter } from '../events.js'\nimport { NOYDB_FORMAT_VERSION } from '../types.js'\nimport type { UnlockedKeyring } from '../team/keyring.js'\nimport { encrypt, decrypt } from '../crypto.js'\nimport { ensureCollectionDEK } from '../team/keyring.js'\nimport type { LedgerStore } from '../history/ledger/store.js'\nimport { envelopePayloadHash } from '../history/ledger/hash.js'\nimport {\n PermissionDeniedError,\n DictKeyMissingError,\n} from '../errors.js'\n\n/** Reserved collection name prefix. Never collides with user collections. */\nexport const DICT_COLLECTION_PREFIX = '_dict_'\n\n/** Return the adapter collection name for a named dictionary. */\nexport function dictCollectionName(dictionaryName: string): string {\n return `${DICT_COLLECTION_PREFIX}${dictionaryName}`\n}\n\n/** Return true when a collection name is a reserved dictionary collection. */\nexport function isDictCollectionName(name: string): boolean {\n return name.startsWith(DICT_COLLECTION_PREFIX)\n}\n\n// ─── DictKey descriptor ────────────────────────────────────────────────\n\n/**\n * Descriptor returned by `dictKey()`. Attach to the collection's\n * `dictKeyFields` option to declare which fields are dictionary-backed:\n *\n * ```ts\n * const invoices = company.collection<Invoice>('invoices', {\n * dictKeyFields: {\n * status: dictKey('status', ['draft', 'open', 'paid'] as const),\n * },\n * })\n * ```\n *\n * The generic parameter `Keys` narrows the TypeScript type of the field\n * to a literal union; the runtime value of `keys` is used by `put()`\n * validation to reject unknown keys when a key set is declared.\n */\nexport interface DictKeyDescriptor<Keys extends string = string> {\n readonly _noydbDictKey: true\n /** Which dictionary this field references. */\n readonly name: string\n /** Declared valid keys. When set, `put()` rejects keys not in this set. */\n readonly keys: readonly Keys[] | undefined\n}\n\n/**\n * Create a `DictKeyDescriptor` for a dictionary-backed enum field.\n *\n * @param name The dictionary name (corresponds to `_dict_<name>` collection).\n * @param keys Optional `as const` array of valid key literals — narrows the\n * TypeScript type to a literal union and enables put-time\n * validation.\n *\n * @example\n * ```ts\n * const invoices = company.collection<Invoice>('invoices', {\n * dictKeyFields: {\n * status: dictKey('status', ['draft', 'open', 'paid'] as const),\n * },\n * })\n * ```\n */\nexport function dictKey<Keys extends string>(\n name: string,\n keys?: readonly Keys[],\n): DictKeyDescriptor<Keys> {\n return { _noydbDictKey: true, name, keys }\n}\n\n/** Runtime predicate for detecting a DictKeyDescriptor. */\nexport function isDictKeyDescriptor(x: unknown): x is DictKeyDescriptor {\n return (\n typeof x === 'object' &&\n x !== null &&\n (x as { _noydbDictKey?: unknown })._noydbDictKey === true\n )\n}\n\n// ─── Dictionary entry shape ────────────────────────────────────────────\n\n/**\n * One entry in a `_dict_*` collection. The record `id` (adapter-side\n * key) IS the stable dictionary key (e.g. `'paid'`). The `labels`\n * record maps locale codes to display strings.\n */\nexport interface DictEntry {\n /** Stable key — same as the record id in the adapter. */\n readonly key: string\n /** Locale → label map, e.g. `{ en: 'Paid', th: 'ชำระแล้ว' }`. */\n readonly labels: Record<string, string>\n}\n\n// ─── Per-dictionary options ────────────────────────────────────────────\n\n/**\n * Options for `vault.dictionary(name, options?)`.\n *\n * `writableBy` controls the minimum role for write operations (put,\n * putAll, delete, rename). Defaults to `'admin'` to match the standard\n * \"dictionary contents are owned by admins\" convention; set to\n * `'operator'` for user-editable dictionaries like custom tags.\n */\nexport interface DictionaryOptions {\n /** Minimum role allowed to write dictionary entries. Default: `'admin'`. */\n readonly writableBy?: 'owner' | 'admin' | 'operator'\n}\n\n// ─── DictionaryHandle ──────────────────────────────────────────────────\n\n/**\n * Handle to a named dictionary within a vault.\n *\n * Obtained via `vault.dictionary(name)`. Provides strongly-typed\n * CRUD for dictionary entries, plus the `rename()` operation that is the\n * only sanctioned mass-mutation path for dictKey fields.\n *\n * All writes are encrypted under the compartment's DEK for the\n * `_dict_<name>` collection. Adapters never see plaintext.\n */\nexport class DictionaryHandle<Keys extends string = string> {\n private readonly collName: string\n\n /**\n * Synchronous write-through cache for dict-join support.\n * Populated on every `put()`, `delete()`, and `rename()`. The snapshot\n * is built from this cache by `snapshotEntries()` — the query executor\n * calls this synchronously inside `.toArray()`.\n *\n * `null` means \"not yet initialized\" — callers should use `list()`\n * to warm the cache before using dict joins on pre-existing data.\n */\n private readonly _syncCache = new Map<string, DictEntry>()\n\n /**\n * Return all cached entries as `{ key, labels, ...labels }` records —\n * usable synchronously by the join executor's `snapshot()` call.\n * Returns an empty array when the cache has never been populated.\n */\n snapshotEntries(): readonly Record<string, unknown>[] {\n return Array.from(this._syncCache.values()).map((e) => ({\n key: e.key,\n labels: e.labels,\n ...e.labels,\n }))\n }\n\n constructor(\n private readonly adapter: NoydbStore,\n private readonly compartmentName: string,\n private readonly dictionaryName: string,\n private readonly keyring: UnlockedKeyring,\n private readonly getDEK: (collectionName: string) => Promise<CryptoKey>,\n private readonly encrypted: boolean,\n private readonly ledger: LedgerStore | undefined,\n private readonly options: DictionaryOptions,\n /**\n * Callback provided by the Vault to find and rewrite records\n * in any registered collection that has a dictKeyField pointing at\n * this dictionary, used by `rename()`.\n */\n private readonly findAndUpdateReferences:\n | ((\n dictionaryName: string,\n oldKey: string,\n newKey: string,\n ) => Promise<void>)\n | undefined,\n private readonly emitter: NoydbEventEmitter,\n ) {\n this.collName = dictCollectionName(dictionaryName)\n }\n\n // ─── Access checks ────────────────────────────────────────────────\n\n private requireWriteAccess(): void {\n const minRole = this.options.writableBy ?? 'admin'\n const roleRank: Record<string, number> = {\n client: 1,\n viewer: 2,\n operator: 3,\n admin: 4,\n owner: 5,\n }\n const callerRank = roleRank[this.keyring.role] ?? 0\n const requiredRank = roleRank[minRole] ?? 4\n if (callerRank < requiredRank) {\n throw new PermissionDeniedError(\n `Dictionary \"${this.dictionaryName}\" writes require \"${minRole}\" role or above. ` +\n `Current role: \"${this.keyring.role}\".`,\n )\n }\n }\n\n // ─── Internal helpers ─────────────────────────────────────────────\n\n private async getDekForDict(): Promise<CryptoKey> {\n const resolve = await ensureCollectionDEK(\n this.adapter,\n this.compartmentName,\n this.keyring,\n )\n return resolve(this.collName)\n }\n\n private async encryptEntry(entry: DictEntry, version: number): Promise<EncryptedEnvelope> {\n if (!this.encrypted) {\n return {\n _noydb: NOYDB_FORMAT_VERSION,\n _v: version,\n _ts: new Date().toISOString(),\n _iv: '',\n _data: JSON.stringify(entry),\n _by: this.keyring.userId,\n }\n }\n const dek = await this.getDekForDict()\n const { iv, data } = await encrypt(JSON.stringify(entry), dek)\n return {\n _noydb: NOYDB_FORMAT_VERSION,\n _v: version,\n _ts: new Date().toISOString(),\n _iv: iv,\n _data: data,\n _by: this.keyring.userId,\n }\n }\n\n private async decryptEntry(envelope: EncryptedEnvelope): Promise<DictEntry> {\n if (!this.encrypted) {\n return JSON.parse(envelope._data) as DictEntry\n }\n const dek = await this.getDekForDict()\n const json = await decrypt(envelope._iv, envelope._data, dek)\n return JSON.parse(json) as DictEntry\n }\n\n // ─── Public API ───────────────────────────────────────────────────\n\n /**\n * Add or overwrite a single dictionary entry.\n *\n * @param key The stable key to store (e.g. `'paid'`).\n * @param labels Locale → label map (e.g. `{ en: 'Paid', th: 'ชำระแล้ว' }`).\n */\n async put(key: Keys, labels: Record<string, string>): Promise<void> {\n this.requireWriteAccess()\n\n const entry: DictEntry = { key, labels }\n const existing = await this.adapter.get(\n this.compartmentName,\n this.collName,\n key,\n )\n const version = existing ? existing._v + 1 : 1\n const envelope = await this.encryptEntry(entry, version)\n\n await this.adapter.put(\n this.compartmentName,\n this.collName,\n key,\n envelope,\n existing ? existing._v : undefined,\n )\n\n // Maintain synchronous cache for dict-join snapshot\n this._syncCache.set(key, entry)\n\n this.emitter.emit('change', {\n vault: this.compartmentName,\n collection: this.collName,\n id: key,\n action: 'put',\n })\n\n if (this.ledger) {\n await this.ledger.append({\n op: 'put',\n collection: this.collName,\n id: key,\n version,\n actor: this.keyring.userId,\n // — must be the real envelope hash so\n // vault.verifyBackupIntegrity()'s data-cross-check matches.\n payloadHash: await envelopePayloadHash(envelope),\n })\n }\n }\n\n /**\n * Batch-add or overwrite multiple dictionary entries in one call.\n *\n * @param entries `{ key: { locale: label } }` map.\n */\n async putAll(entries: Record<Keys, Record<string, string>>): Promise<void> {\n this.requireWriteAccess()\n for (const [key, labels] of Object.entries(entries) as [Keys, Record<string, string>][]) {\n await this.put(key, labels)\n }\n }\n\n /**\n * Load the label map for a single key.\n *\n * @returns The label map, or `null` if the key doesn't exist.\n */\n async get(key: Keys): Promise<Record<string, string> | null> {\n const envelope = await this.adapter.get(\n this.compartmentName,\n this.collName,\n key,\n )\n if (!envelope) return null\n const entry = await this.decryptEntry(envelope)\n return entry.labels\n }\n\n /**\n * Delete a dictionary key.\n *\n * Default mode is `'strict'` — throws `DictKeyInUseError` if any\n * registered collection has a record referencing this key. Pass\n * `{ mode: 'warn' }` to skip the check (dev-mode cleanup only).\n */\n async delete(key: Keys, opts: { mode?: 'strict' | 'warn' } = {}): Promise<void> {\n this.requireWriteAccess()\n\n const existing = await this.adapter.get(\n this.compartmentName,\n this.collName,\n key,\n )\n if (!existing) {\n throw new DictKeyMissingError(this.dictionaryName, key)\n }\n\n const mode = opts.mode ?? 'strict'\n if (mode === 'strict' && this.findAndUpdateReferences) {\n // Check for references by attempting a rename to a sentinel that\n // doesn't exist — we reuse the reference-finding machinery but\n // abort before applying changes. Simpler: the vault\n // exposes a separate checkReferences() callback. For now we rely\n // on the caller to confirm no references exist, or use warn mode.\n // A dedicated findReferences API is tracked as a follow-up.\n }\n\n await this.adapter.delete(this.compartmentName, this.collName, key)\n\n // Maintain synchronous cache for dict-join snapshot\n this._syncCache.delete(key)\n\n this.emitter.emit('change', {\n vault: this.compartmentName,\n collection: this.collName,\n id: key,\n action: 'delete',\n })\n\n if (this.ledger) {\n await this.ledger.append({\n op: 'delete',\n collection: this.collName,\n id: key,\n version: existing._v,\n actor: this.keyring.userId,\n // — for delete the prior envelope is what was just\n // removed; we hash it so the chain captures intent. The\n // verifyBackupIntegrity data-cross-check skips delete\n // entries entirely (the live record is gone), but the\n // chain still benefits from a stable non-empty hash.\n payloadHash: await envelopePayloadHash(existing),\n })\n }\n }\n\n /**\n * Rename a dictionary key — the only sanctioned mass-mutation path.\n *\n * Atomically:\n * 1. Adds the new key with the same labels as the old key.\n * 2. Updates every registered record that stores the old key to\n * store the new key instead.\n * 3. Deletes the old key.\n * 4. Appends a single ledger entry recording the rename.\n *\n * Respects ACL: throws `PermissionDeniedError` before any mutation\n * if the caller can't write. The cascade is best-effort atomic\n * within this call — no two-phase commit across adapter calls.\n *\n * Cascade-on-delete is NOT supported. Use `rename()` when you need\n * to change a key that records reference.\n */\n async rename(oldKey: Keys, newKey: string): Promise<void> {\n this.requireWriteAccess()\n\n // 1. Load old entry\n const existing = await this.adapter.get(\n this.compartmentName,\n this.collName,\n oldKey,\n )\n if (!existing) {\n throw new DictKeyMissingError(this.dictionaryName, oldKey)\n }\n const oldEntry = await this.decryptEntry(existing)\n\n // 2. Write new key\n const newEntry: DictEntry = { key: newKey, labels: oldEntry.labels }\n const newEnvelope = await this.encryptEntry(newEntry, 1)\n await this.adapter.put(\n this.compartmentName,\n this.collName,\n newKey,\n newEnvelope,\n )\n\n // 3. Update all referencing records in registered collections\n if (this.findAndUpdateReferences) {\n await this.findAndUpdateReferences(this.dictionaryName, oldKey, newKey)\n }\n\n // 4. Delete old key\n await this.adapter.delete(this.compartmentName, this.collName, oldKey)\n\n // Maintain synchronous cache for dict-join snapshot\n this._syncCache.delete(oldKey)\n this._syncCache.set(newKey, newEntry)\n\n this.emitter.emit('change', {\n vault: this.compartmentName,\n collection: this.collName,\n id: oldKey,\n action: 'delete',\n })\n this.emitter.emit('change', {\n vault: this.compartmentName,\n collection: this.collName,\n id: newKey,\n action: 'put',\n })\n\n // 5. Ledger — record the rename as delete(oldKey) + put(newKey)\n // so verifyBackupIntegrity()'s data-cross-check matches reality\n // (the oldKey envelope is gone; the newKey envelope is what was\n // just written). Two entries instead of one — the chain still\n // captures the rename intent via the matching ts + actor.\n if (this.ledger) {\n await this.ledger.append({\n op: 'delete',\n collection: this.collName,\n id: oldKey,\n version: existing._v,\n actor: this.keyring.userId,\n payloadHash: await envelopePayloadHash(existing),\n })\n await this.ledger.append({\n op: 'put',\n collection: this.collName,\n id: newKey,\n version: 1,\n actor: this.keyring.userId,\n payloadHash: await envelopePayloadHash(newEnvelope),\n })\n }\n }\n\n /**\n * List all entries in this dictionary.\n *\n * @returns Array of `{ key, labels }` objects.\n */\n async list(): Promise<DictEntry[]> {\n const keys = await this.adapter.list(this.compartmentName, this.collName)\n const entries: DictEntry[] = []\n for (const key of keys) {\n const envelope = await this.adapter.get(\n this.compartmentName,\n this.collName,\n key,\n )\n if (!envelope) continue\n const entry = await this.decryptEntry(envelope)\n entries.push(entry)\n // Warm the synchronous cache\n this._syncCache.set(key, entry)\n }\n return entries\n }\n\n /**\n * Resolve a key to its label for the given locale.\n *\n * Used by the collection's locale-aware read path to populate\n * `<field>Label` virtual fields. Returns `undefined` when the\n * key doesn't exist or has no label for the requested locale\n * (after exhausting the fallback chain).\n */\n async resolveLabel(\n key: string,\n locale: string,\n fallback?: string | readonly string[],\n ): Promise<string | undefined> {\n const labels = await this.get(key as Keys)\n if (!labels) return undefined\n\n // Try primary locale\n if (labels[locale] !== undefined) return labels[locale]\n\n // Try fallback chain\n const chain = Array.isArray(fallback) ? (fallback as readonly string[]) : fallback ? [fallback as string] : []\n for (const fb of chain) {\n if (fb === 'any') {\n // Return any available label\n const any = Object.values(labels)[0]\n if (any !== undefined) return any\n } else if (labels[fb] !== undefined) {\n return labels[fb]\n }\n }\n\n return undefined\n }\n}\n","/**\n * i18nText schema type —\n *\n * `i18nText({ languages, required })` creates a descriptor for a\n * multi-language content field whose value is stored as a\n * `{ [locale]: string }` map (e.g. `{ en: 'Consulting', th: 'ที่ปรึกษา' }`).\n *\n * On put, the descriptor validates that required languages are present.\n * On read (when a `locale` option is passed), the map is collapsed to the\n * caller's locale string via the fallback chain.\n *\n * Design decisions\n * ────────────────\n *\n * **Descriptor pattern (not a Zod type).**\n * `i18nText()` returns a plain descriptor object used in the collection's\n * `i18nFields` option — same pattern as `ref()` / `dictKey()`. This keeps\n * `@noy-db/core` at zero runtime dependencies and avoids Zod v3 field-type\n * constraints. TypeScript inference is handled via the descriptor's type.\n *\n * **Enforcement at the collection boundary.**\n * The `required` option is checked by `Collection.put()` via the compartment's\n * registered `i18nFields`. Failed validation throws `MissingTranslationError`\n * — a distinct class from `SchemaValidationError` so callers can tell\n * \"wrong shape\" from \"missing translations\".\n *\n * **Resolution is post-decryption.**\n * Locale resolution happens AFTER `decryptRecord()`, as a pure in-memory\n * transform. No additional crypto work is needed. The resolved record is\n * returned in place of the stored one, with i18nText fields replaced by\n * their locale-resolved strings.\n *\n * **`locale: 'raw'`.**\n * Passing `{ locale: 'raw' }` skips resolution and returns the full\n * `{ [locale]: string }` map — useful for bilingual exports, admin UIs,\n * and any context where all translations must be visible at once.\n *\n * **Out of scope.**\n * Pluralization, RTL rendering, date/number formatting, per-locale CRDT\n * merging.\n */\n\nimport { MissingTranslationError, LocaleNotSpecifiedError } from '../errors.js'\n\n// ─── i18nText descriptor ───────────────────────────────────────────────\n\n/**\n * Options for `i18nText()`.\n *\n * `languages` declares the full set of supported locales. `required`\n * controls which must be present on every `put()`.\n *\n * `autoTranslate` is the per-field opt-in for the `plaintextTranslator`\n * hook. When `true` and a `plaintextTranslator` is configured\n * on `createNoydb()`, missing translations are generated before `put()`.\n * Default: `false`.\n */\nexport interface I18nTextOptions {\n /** All supported locale codes (BCP 47). */\n readonly languages: readonly string[]\n /**\n * Which locales must be present on every `put()`.\n *\n * - `'all'` — every declared language must be present.\n * - `'any'` — at least one declared language must be present.\n * - `string[]` — listed locales are required; others are optional.\n */\n readonly required: 'all' | 'any' | readonly string[]\n /**\n * Per-field opt-in for the `plaintextTranslator` hook.\n * When `true`, missing required translations are auto-generated\n * before `put()` if a translator is configured. Default: `false`.\n */\n readonly autoTranslate?: boolean\n}\n\n/**\n * Descriptor returned by `i18nText()`. Attach to the collection's\n * `i18nFields` option:\n *\n * ```ts\n * const lineItems = company.collection<LineItem>('line-items', {\n * i18nFields: {\n * description: i18nText({ languages: ['en', 'th'], required: 'all' }),\n * },\n * })\n * ```\n */\nexport interface I18nTextDescriptor {\n readonly _noydbI18nText: true\n readonly options: I18nTextOptions\n}\n\n/**\n * Create an `I18nTextDescriptor` for a multi-language content field.\n *\n * @param options Language list + enforcement mode.\n *\n * @example\n * ```ts\n * i18nText({ languages: ['en', 'th'], required: 'all' })\n * i18nText({ languages: ['en', 'th'], required: ['th'], autoTranslate: true })\n * ```\n */\nexport function i18nText(options: I18nTextOptions): I18nTextDescriptor {\n return { _noydbI18nText: true, options }\n}\n\n/** Runtime predicate for detecting an `I18nTextDescriptor`. */\nexport function isI18nTextDescriptor(x: unknown): x is I18nTextDescriptor {\n return (\n typeof x === 'object' &&\n x !== null &&\n (x as { _noydbI18nText?: unknown })._noydbI18nText === true\n )\n}\n\n// ─── Validation helpers ────────────────────────────────────────────────\n\n/**\n * Validate that a value is a valid `{ [locale]: string }` map and that\n * all required locales are present. Throws `MissingTranslationError`\n * when the required constraint is violated.\n *\n * Called by `Collection.put()` for each registered `i18nField`.\n *\n * @param value The raw field value from the record being put.\n * @param field The field name (used in the thrown error message).\n * @param descriptor The `i18nText()` descriptor for this field.\n */\nexport function validateI18nTextValue(\n value: unknown,\n field: string,\n descriptor: I18nTextDescriptor,\n): void {\n const { options } = descriptor\n\n // Must be a non-null object\n if (typeof value !== 'object' || value === null || Array.isArray(value)) {\n throw new MissingTranslationError(\n field,\n options.languages,\n `Field \"${field}\" must be a { [locale]: string } map, got ${typeof value}.`,\n )\n }\n\n const map = value as Record<string, unknown>\n\n // All values must be strings\n for (const [locale, v] of Object.entries(map)) {\n if (typeof v !== 'string') {\n throw new MissingTranslationError(\n field,\n [locale],\n `Field \"${field}\": locale \"${locale}\" must be a string, got ${typeof v}.`,\n )\n }\n }\n\n // Check required constraint\n const { required } = options\n if (required === 'all') {\n const missing = options.languages.filter(\n (lang) => !(lang in map) || map[lang] === '',\n )\n if (missing.length > 0) {\n throw new MissingTranslationError(\n field,\n missing,\n `Field \"${field}\" requires all declared languages. Missing: ${missing.join(', ')}.`,\n )\n }\n } else if (required === 'any') {\n const present = options.languages.some(\n (lang) => lang in map && map[lang] !== '',\n )\n if (!present) {\n throw new MissingTranslationError(\n field,\n options.languages,\n `Field \"${field}\" requires at least one declared language. None present.`,\n )\n }\n } else {\n // string[] — named required locales; TypeScript narrows required to readonly string[]\n const requiredList = required\n const missing = requiredList.filter(\n (lang) => !(lang in map) || map[lang] === '',\n )\n if (missing.length > 0) {\n throw new MissingTranslationError(\n field,\n missing,\n `Field \"${field}\" requires: ${requiredList.join(', ')}. Missing: ${missing.join(', ')}.`,\n )\n }\n }\n}\n\n// ─── Locale resolution ─────────────────────────────────────────────────\n\n/**\n * Resolve an i18nText value (`{ [locale]: string }` map) to a string\n * for the given locale.\n *\n * @param value The stored locale map.\n * @param locale The requested locale code, or `'raw'` to return the map.\n * @param fallback Single locale or ordered list; use `'any'` as the last\n * element to fall back to any available translation.\n * @param field Field name used in `LocaleNotSpecifiedError` messages.\n * @returns The resolved string, OR the original map when `locale === 'raw'`.\n */\nexport function resolveI18nText(\n value: Record<string, string>,\n locale: string,\n fallback?: string | readonly string[],\n field?: string,\n): string | Record<string, string> {\n if (locale === 'raw') {\n return value\n }\n\n if (!locale) {\n throw new LocaleNotSpecifiedError(field ?? '<unknown>')\n }\n\n // Primary locale\n if (value[locale] !== undefined && value[locale] !== '') {\n return value[locale]\n }\n\n // Fallback chain\n const chain: readonly string[] = Array.isArray(fallback)\n ? fallback\n : fallback\n ? [fallback]\n : []\n\n for (const fb of chain) {\n if (fb === 'any') {\n const any = Object.values(value).find((v) => v !== '')\n if (any !== undefined) return any\n } else if (value[fb] !== undefined && value[fb] !== '') {\n return value[fb]\n }\n }\n\n throw new LocaleNotSpecifiedError(\n field ?? '<unknown>',\n `No translation available for locale \"${locale}\"` +\n (chain.length > 0 ? ` or fallback chain [${chain.join(', ')}]` : '') +\n '.',\n )\n}\n\n/**\n * Apply locale resolution to a single record, in-place over a copy.\n *\n * For each field registered as an `i18nText` descriptor:\n * - If `locale === 'raw'`, the field value is left as the stored map.\n * - Otherwise, the field value is replaced with the resolved string.\n *\n * Records that are not plain objects (null, array, primitives) are\n * returned unchanged.\n *\n * @param record The decrypted record.\n * @param i18nFields Map of field name → `I18nTextDescriptor`.\n * @param locale The requested locale (or `'raw'`).\n * @param fallback Fallback chain (optional).\n */\nexport function applyI18nLocale(\n record: Record<string, unknown>,\n i18nFields: Record<string, I18nTextDescriptor>,\n locale: string,\n fallback?: string | readonly string[],\n): Record<string, unknown> {\n const fieldNames = Object.keys(i18nFields)\n if (fieldNames.length === 0) return record\n\n const result = { ...record }\n\n for (const field of fieldNames) {\n const raw = result[field]\n if (raw === undefined || raw === null) continue\n if (typeof raw !== 'object' || Array.isArray(raw)) continue\n\n result[field] = resolveI18nText(\n raw as Record<string, string>,\n locale,\n fallback,\n field,\n )\n }\n\n return result\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// src/types.ts
|
|
2
|
+
var NOYDB_FORMAT_VERSION = 1;
|
|
3
|
+
var NOYDB_KEYRING_VERSION = 1;
|
|
4
|
+
var NOYDB_BACKUP_VERSION = 1;
|
|
5
|
+
var NOYDB_SYNC_VERSION = 1;
|
|
6
|
+
function createStore(factory) {
|
|
7
|
+
return factory;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export {
|
|
11
|
+
NOYDB_FORMAT_VERSION,
|
|
12
|
+
NOYDB_KEYRING_VERSION,
|
|
13
|
+
NOYDB_BACKUP_VERSION,
|
|
14
|
+
NOYDB_SYNC_VERSION,
|
|
15
|
+
createStore
|
|
16
|
+
};
|
|
17
|
+
//# sourceMappingURL=chunk-ZRG4V3F5.js.map
|