js-bao 0.4.2 → 0.5.1
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/browser.cjs +206 -41
- package/dist/browser.d.cts +41 -0
- package/dist/browser.d.ts +41 -0
- package/dist/browser.js +203 -39
- package/dist/client.cjs +15 -4
- package/dist/client.d.cts +78 -0
- package/dist/client.d.ts +78 -0
- package/dist/client.js +12 -2
- package/dist/cloudflare-do.cjs +191 -26
- package/dist/cloudflare-do.d.cts +127 -1
- package/dist/cloudflare-do.d.ts +127 -1
- package/dist/cloudflare-do.js +188 -24
- package/dist/cloudflare.cjs +78 -21
- package/dist/cloudflare.d.cts +84 -0
- package/dist/cloudflare.d.ts +84 -0
- package/dist/cloudflare.js +75 -19
- package/dist/codegen-v2.cjs +80 -9
- package/dist/codegen.cjs +6 -1
- package/dist/index.cjs +210 -45
- package/dist/index.d.cts +41 -0
- package/dist/index.d.ts +41 -0
- package/dist/index.js +207 -43
- package/dist/node.cjs +210 -45
- package/dist/node.d.cts +41 -0
- package/dist/node.d.ts +41 -0
- package/dist/node.js +207 -43
- package/package.json +1 -1
package/dist/cloudflare.cjs
CHANGED
|
@@ -84,9 +84,25 @@ var init_DocumentQueryTranslator = __esm({
|
|
|
84
84
|
}
|
|
85
85
|
});
|
|
86
86
|
|
|
87
|
+
// src/utils/yjsValues.ts
|
|
88
|
+
function isStringSetYMap(value) {
|
|
89
|
+
if (!(value instanceof Y.Map)) return false;
|
|
90
|
+
for (const v of value.values()) {
|
|
91
|
+
if (v !== true) return false;
|
|
92
|
+
}
|
|
93
|
+
return true;
|
|
94
|
+
}
|
|
95
|
+
var Y;
|
|
96
|
+
var init_yjsValues = __esm({
|
|
97
|
+
"src/utils/yjsValues.ts"() {
|
|
98
|
+
"use strict";
|
|
99
|
+
Y = __toESM(require("yjs"), 1);
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
|
|
87
103
|
// src/models/metaSync.ts
|
|
88
104
|
function inferFieldType(value) {
|
|
89
|
-
if (value
|
|
105
|
+
if (isStringSetYMap(value)) return "stringset";
|
|
90
106
|
switch (typeof value) {
|
|
91
107
|
case "string":
|
|
92
108
|
return "string";
|
|
@@ -131,7 +147,7 @@ function syncModelMeta(yDoc, modelName, schema) {
|
|
|
131
147
|
if (compoundConstraints.length > 0) {
|
|
132
148
|
let constraints = meta.get("_constraints");
|
|
133
149
|
if (!constraints) {
|
|
134
|
-
constraints = new
|
|
150
|
+
constraints = new Y2.Map();
|
|
135
151
|
meta.set("_constraints", constraints);
|
|
136
152
|
}
|
|
137
153
|
for (const constraint of compoundConstraints) {
|
|
@@ -142,7 +158,7 @@ function syncModelMeta(yDoc, modelName, schema) {
|
|
|
142
158
|
if (relationships && Object.keys(relationships).length > 0) {
|
|
143
159
|
let rels = meta.get("_relationships");
|
|
144
160
|
if (!rels) {
|
|
145
|
-
rels = new
|
|
161
|
+
rels = new Y2.Map();
|
|
146
162
|
meta.set("_relationships", rels);
|
|
147
163
|
}
|
|
148
164
|
for (const [relName, relConfig] of Object.entries(relationships)) {
|
|
@@ -154,7 +170,7 @@ function syncModelMeta(yDoc, modelName, schema) {
|
|
|
154
170
|
function syncFieldMeta(metaMap, fieldName, fieldOpts) {
|
|
155
171
|
let fieldMeta = metaMap.get(fieldName);
|
|
156
172
|
if (!fieldMeta) {
|
|
157
|
-
fieldMeta = new
|
|
173
|
+
fieldMeta = new Y2.Map();
|
|
158
174
|
metaMap.set(fieldName, fieldMeta);
|
|
159
175
|
}
|
|
160
176
|
setIfChanged(fieldMeta, "type", fieldOpts.type);
|
|
@@ -162,6 +178,7 @@ function syncFieldMeta(metaMap, fieldName, fieldOpts) {
|
|
|
162
178
|
if (fieldOpts.unique) setIfChanged(fieldMeta, "unique", true);
|
|
163
179
|
if (fieldOpts.required) setIfChanged(fieldMeta, "required", true);
|
|
164
180
|
if (fieldOpts.autoAssign) setIfChanged(fieldMeta, "autoAssign", true);
|
|
181
|
+
if (fieldOpts.autoStamp) setIfChanged(fieldMeta, "autoStamp", fieldOpts.autoStamp);
|
|
165
182
|
if (fieldOpts.maxLength !== void 0) setIfChanged(fieldMeta, "maxLength", fieldOpts.maxLength);
|
|
166
183
|
if (fieldOpts.maxCount !== void 0) setIfChanged(fieldMeta, "maxCount", fieldOpts.maxCount);
|
|
167
184
|
const encoded = encodeDefault(fieldOpts.default);
|
|
@@ -170,7 +187,7 @@ function syncFieldMeta(metaMap, fieldName, fieldOpts) {
|
|
|
170
187
|
function syncConstraintMeta(constraintsMap, constraint) {
|
|
171
188
|
let cMeta = constraintsMap.get(constraint.name);
|
|
172
189
|
if (!cMeta) {
|
|
173
|
-
cMeta = new
|
|
190
|
+
cMeta = new Y2.Map();
|
|
174
191
|
constraintsMap.set(constraint.name, cMeta);
|
|
175
192
|
}
|
|
176
193
|
setIfChanged(cMeta, "type", "unique");
|
|
@@ -180,7 +197,7 @@ function syncConstraintMeta(constraintsMap, constraint) {
|
|
|
180
197
|
function syncRelationshipMeta(relsMap, relName, relConfig) {
|
|
181
198
|
let relMeta = relsMap.get(relName);
|
|
182
199
|
if (!relMeta) {
|
|
183
|
-
relMeta = new
|
|
200
|
+
relMeta = new Y2.Map();
|
|
184
201
|
relsMap.set(relName, relMeta);
|
|
185
202
|
}
|
|
186
203
|
for (const [key, value] of Object.entries(relConfig)) {
|
|
@@ -195,7 +212,7 @@ function syncInferredMeta(yDoc, modelName, recordData) {
|
|
|
195
212
|
if (fieldName.startsWith("_")) continue;
|
|
196
213
|
let fieldMeta = meta.get(fieldName);
|
|
197
214
|
if (!fieldMeta) {
|
|
198
|
-
fieldMeta = new
|
|
215
|
+
fieldMeta = new Y2.Map();
|
|
199
216
|
meta.set(fieldName, fieldMeta);
|
|
200
217
|
}
|
|
201
218
|
if (!fieldMeta.has("type")) {
|
|
@@ -211,11 +228,12 @@ function setIfChanged(map, key, value) {
|
|
|
211
228
|
map.set(key, value);
|
|
212
229
|
}
|
|
213
230
|
}
|
|
214
|
-
var
|
|
231
|
+
var Y2, KNOWN_FUNCTION_DEFAULTS, _syncedCache;
|
|
215
232
|
var init_metaSync = __esm({
|
|
216
233
|
"src/models/metaSync.ts"() {
|
|
217
234
|
"use strict";
|
|
218
|
-
|
|
235
|
+
Y2 = __toESM(require("yjs"), 1);
|
|
236
|
+
init_yjsValues();
|
|
219
237
|
KNOWN_FUNCTION_DEFAULTS = /* @__PURE__ */ new WeakMap();
|
|
220
238
|
_syncedCache = /* @__PURE__ */ new WeakMap();
|
|
221
239
|
}
|
|
@@ -225,11 +243,11 @@ var init_metaSync = __esm({
|
|
|
225
243
|
function generateULID() {
|
|
226
244
|
return (0, import_ulid.ulid)();
|
|
227
245
|
}
|
|
228
|
-
var
|
|
246
|
+
var Y3, import_ulid, Logger;
|
|
229
247
|
var init_BaseModel = __esm({
|
|
230
248
|
"src/models/BaseModel.ts"() {
|
|
231
249
|
"use strict";
|
|
232
|
-
|
|
250
|
+
Y3 = __toESM(require("yjs"), 1);
|
|
233
251
|
import_ulid = require("ulid");
|
|
234
252
|
init_StringSet();
|
|
235
253
|
init_documentTypes();
|
|
@@ -237,6 +255,7 @@ var init_BaseModel = __esm({
|
|
|
237
255
|
init_CursorManager();
|
|
238
256
|
init_sql();
|
|
239
257
|
init_metaSync();
|
|
258
|
+
init_yjsValues();
|
|
240
259
|
Logger = class {
|
|
241
260
|
static _logLevel = 1 /* ERROR */;
|
|
242
261
|
static _logCallback = null;
|
|
@@ -1223,7 +1242,8 @@ function connectDoDb(options) {
|
|
|
1223
1242
|
}
|
|
1224
1243
|
|
|
1225
1244
|
// src/utils/yDocSchema.ts
|
|
1226
|
-
var
|
|
1245
|
+
var Y4 = __toESM(require("yjs"), 1);
|
|
1246
|
+
init_yjsValues();
|
|
1227
1247
|
function discoverSchema(yDoc) {
|
|
1228
1248
|
const models = {};
|
|
1229
1249
|
const metaNames = /* @__PURE__ */ new Set();
|
|
@@ -1257,7 +1277,7 @@ function discoverModelNames(yDoc) {
|
|
|
1257
1277
|
function materializeMap(yDoc, key) {
|
|
1258
1278
|
try {
|
|
1259
1279
|
const map = yDoc.getMap(key);
|
|
1260
|
-
return map instanceof
|
|
1280
|
+
return map instanceof Y4.Map ? map : null;
|
|
1261
1281
|
} catch {
|
|
1262
1282
|
return null;
|
|
1263
1283
|
}
|
|
@@ -1267,11 +1287,11 @@ function readModelMeta(metaMap) {
|
|
|
1267
1287
|
let constraints;
|
|
1268
1288
|
let relationships;
|
|
1269
1289
|
for (const [key, value] of metaMap.entries()) {
|
|
1270
|
-
if (key === "_constraints" && value instanceof
|
|
1290
|
+
if (key === "_constraints" && value instanceof Y4.Map) {
|
|
1271
1291
|
constraints = readConstraints(value);
|
|
1272
|
-
} else if (key === "_relationships" && value instanceof
|
|
1292
|
+
} else if (key === "_relationships" && value instanceof Y4.Map) {
|
|
1273
1293
|
relationships = readRelationships(value);
|
|
1274
|
-
} else if (value instanceof
|
|
1294
|
+
} else if (value instanceof Y4.Map) {
|
|
1275
1295
|
fields[key] = readFieldMeta(value);
|
|
1276
1296
|
}
|
|
1277
1297
|
}
|
|
@@ -1290,6 +1310,10 @@ function readFieldMeta(fieldMap) {
|
|
|
1290
1310
|
if (fieldMap.get("unique") === true) field.unique = true;
|
|
1291
1311
|
if (fieldMap.get("required") === true) field.required = true;
|
|
1292
1312
|
if (fieldMap.get("autoAssign") === true) field.autoAssign = true;
|
|
1313
|
+
const autoStamp = fieldMap.get("autoStamp");
|
|
1314
|
+
if (typeof autoStamp === "string" && (autoStamp === "create" || autoStamp === "update" || autoStamp === "both")) {
|
|
1315
|
+
field.autoStamp = autoStamp;
|
|
1316
|
+
}
|
|
1293
1317
|
const def = fieldMap.get("default");
|
|
1294
1318
|
if (def !== void 0) field.default = def;
|
|
1295
1319
|
const maxLength = fieldMap.get("maxLength");
|
|
@@ -1302,7 +1326,7 @@ function inferModelFromData(dataMap) {
|
|
|
1302
1326
|
const fields = {};
|
|
1303
1327
|
let sampled = 0;
|
|
1304
1328
|
for (const [_recordId, recordValue] of dataMap.entries()) {
|
|
1305
|
-
if (!(recordValue instanceof
|
|
1329
|
+
if (!(recordValue instanceof Y4.Map)) continue;
|
|
1306
1330
|
if (++sampled > 5) break;
|
|
1307
1331
|
for (const [fieldName, value] of recordValue.entries()) {
|
|
1308
1332
|
if (fieldName.startsWith("_")) continue;
|
|
@@ -1315,7 +1339,7 @@ function inferModelFromData(dataMap) {
|
|
|
1315
1339
|
return { fields };
|
|
1316
1340
|
}
|
|
1317
1341
|
function inferTypeFromValue(value) {
|
|
1318
|
-
if (value
|
|
1342
|
+
if (isStringSetYMap(value)) return "stringset";
|
|
1319
1343
|
switch (typeof value) {
|
|
1320
1344
|
case "string":
|
|
1321
1345
|
return "string";
|
|
@@ -1330,7 +1354,7 @@ function inferTypeFromValue(value) {
|
|
|
1330
1354
|
function readConstraints(constraintsMap) {
|
|
1331
1355
|
const out = {};
|
|
1332
1356
|
for (const [name, value] of constraintsMap.entries()) {
|
|
1333
|
-
if (!(value instanceof
|
|
1357
|
+
if (!(value instanceof Y4.Map)) continue;
|
|
1334
1358
|
let fields = [];
|
|
1335
1359
|
const rawFields = value.get("fields");
|
|
1336
1360
|
if (typeof rawFields === "string") {
|
|
@@ -1349,7 +1373,7 @@ function readConstraints(constraintsMap) {
|
|
|
1349
1373
|
function readRelationships(relsMap) {
|
|
1350
1374
|
const out = {};
|
|
1351
1375
|
for (const [name, value] of relsMap.entries()) {
|
|
1352
|
-
if (!(value instanceof
|
|
1376
|
+
if (!(value instanceof Y4.Map)) continue;
|
|
1353
1377
|
const rel = {};
|
|
1354
1378
|
for (const [k, v] of value.entries()) {
|
|
1355
1379
|
rel[k] = v;
|
|
@@ -1360,6 +1384,7 @@ function readRelationships(relsMap) {
|
|
|
1360
1384
|
}
|
|
1361
1385
|
var CAMEL_TO_SNAKE = {
|
|
1362
1386
|
autoAssign: "auto_assign",
|
|
1387
|
+
autoStamp: "auto_stamp",
|
|
1363
1388
|
maxLength: "max_length",
|
|
1364
1389
|
maxCount: "max_count",
|
|
1365
1390
|
relatedIdField: "related_id_field",
|
|
@@ -1390,6 +1415,7 @@ function schemaToToml(schema) {
|
|
|
1390
1415
|
lines.push(`[models.${modelName}.fields.${fieldName}]`);
|
|
1391
1416
|
lines.push(`type = ${tomlValue(field.type)}`);
|
|
1392
1417
|
if (field.autoAssign) lines.push("auto_assign = true");
|
|
1418
|
+
if (field.autoStamp) lines.push(`auto_stamp = ${tomlValue(field.autoStamp)}`);
|
|
1393
1419
|
if (field.indexed) lines.push("indexed = true");
|
|
1394
1420
|
if (field.unique) lines.push("unique = true");
|
|
1395
1421
|
if (field.required) lines.push("required = true");
|
|
@@ -1513,10 +1539,13 @@ var KNOWN_FIELD_KEYS = /* @__PURE__ */ new Set([
|
|
|
1513
1539
|
"unique",
|
|
1514
1540
|
"required",
|
|
1515
1541
|
"auto_assign",
|
|
1542
|
+
"auto_stamp",
|
|
1516
1543
|
"max_length",
|
|
1517
1544
|
"max_count",
|
|
1518
|
-
"default"
|
|
1545
|
+
"default",
|
|
1546
|
+
"enum"
|
|
1519
1547
|
]);
|
|
1548
|
+
var VALID_AUTO_STAMP_VALUES = /* @__PURE__ */ new Set(["create", "update", "both"]);
|
|
1520
1549
|
var KNOWN_MODEL_KEYS = /* @__PURE__ */ new Set([
|
|
1521
1550
|
"fields",
|
|
1522
1551
|
"relationships",
|
|
@@ -1558,9 +1587,37 @@ function parseFieldOptions(raw, context, strict) {
|
|
|
1558
1587
|
if (raw.unique === true) opts.unique = true;
|
|
1559
1588
|
if (raw.required === true) opts.required = true;
|
|
1560
1589
|
if (raw.auto_assign === true) opts.autoAssign = true;
|
|
1590
|
+
if (raw.auto_stamp !== void 0) {
|
|
1591
|
+
if (typeof raw.auto_stamp !== "string" || !VALID_AUTO_STAMP_VALUES.has(raw.auto_stamp)) {
|
|
1592
|
+
throw new Error(
|
|
1593
|
+
`${context}: invalid auto_stamp value "${raw.auto_stamp}". Must be one of: ${[
|
|
1594
|
+
...VALID_AUTO_STAMP_VALUES
|
|
1595
|
+
].join(", ")}`
|
|
1596
|
+
);
|
|
1597
|
+
}
|
|
1598
|
+
opts.autoStamp = raw.auto_stamp;
|
|
1599
|
+
}
|
|
1561
1600
|
if (raw.max_length !== void 0) opts.maxLength = raw.max_length;
|
|
1562
1601
|
if (raw.max_count !== void 0) opts.maxCount = raw.max_count;
|
|
1563
1602
|
if (raw.default !== void 0) opts.default = raw.default;
|
|
1603
|
+
if (raw.enum !== void 0) {
|
|
1604
|
+
if (raw.type !== "string") {
|
|
1605
|
+
throw new Error(
|
|
1606
|
+
`${context}: \`enum\` is only valid on a "string" field, not "${raw.type}".`
|
|
1607
|
+
);
|
|
1608
|
+
}
|
|
1609
|
+
if (!Array.isArray(raw.enum) || raw.enum.length === 0) {
|
|
1610
|
+
throw new Error(
|
|
1611
|
+
`${context}: \`enum\` must be a non-empty array of strings.`
|
|
1612
|
+
);
|
|
1613
|
+
}
|
|
1614
|
+
if (!raw.enum.every((v) => typeof v === "string")) {
|
|
1615
|
+
throw new Error(
|
|
1616
|
+
`${context}: \`enum\` values must all be strings.`
|
|
1617
|
+
);
|
|
1618
|
+
}
|
|
1619
|
+
opts.enum = [...raw.enum];
|
|
1620
|
+
}
|
|
1564
1621
|
return opts;
|
|
1565
1622
|
}
|
|
1566
1623
|
function requireField(raw, field, context) {
|
package/dist/cloudflare.d.cts
CHANGED
|
@@ -31,8 +31,32 @@ interface FieldOptions {
|
|
|
31
31
|
unique?: boolean;
|
|
32
32
|
default?: any | (() => any);
|
|
33
33
|
autoAssign?: boolean;
|
|
34
|
+
/**
|
|
35
|
+
* Auto-timestamp this field with `Date.now()` (milliseconds) on save.
|
|
36
|
+
*
|
|
37
|
+
* - `'create'` — stamp only on the first save (when `isNew === true`).
|
|
38
|
+
* - `'update'` — stamp on every save (create AND update).
|
|
39
|
+
* - `'both'` — stamp on every save (create AND update).
|
|
40
|
+
*
|
|
41
|
+
* If the caller passes an explicit value for the field in `data`, the
|
|
42
|
+
* explicit value wins and the stamp is skipped. The stamp is applied
|
|
43
|
+
* in `beforeSave` BEFORE any user-defined hooks, so user hooks may
|
|
44
|
+
* still overwrite the value if they wish (last writer wins).
|
|
45
|
+
*/
|
|
46
|
+
autoStamp?: "create" | "update" | "both";
|
|
34
47
|
maxLength?: number;
|
|
35
48
|
maxCount?: number;
|
|
49
|
+
/**
|
|
50
|
+
* Allowed-value set for a `string` field. When present, the codegen
|
|
51
|
+
* generators (database-type codegen + doc-model v2 codegen) emit a
|
|
52
|
+
* TypeScript string-literal union (`"a" | "b" | "c"`) instead of a bare
|
|
53
|
+
* `string` for this field.
|
|
54
|
+
*
|
|
55
|
+
* Advisory / codegen-only: this is a TS-emission hint. The runtime and the
|
|
56
|
+
* server do NOT enforce enum membership on write (see #843). Only valid on
|
|
57
|
+
* `string` fields, and must be a non-empty array of strings.
|
|
58
|
+
*/
|
|
59
|
+
enum?: string[];
|
|
36
60
|
}
|
|
37
61
|
interface UniqueConstraintConfig {
|
|
38
62
|
name: string;
|
|
@@ -394,6 +418,17 @@ declare class BaseModel implements StringSetChangeTracker {
|
|
|
394
418
|
private ensureLocalChanges;
|
|
395
419
|
private hasLocalChange;
|
|
396
420
|
private getFromYjs;
|
|
421
|
+
/**
|
|
422
|
+
* Defensive normalization for the read/sync boundary (issue #625).
|
|
423
|
+
*
|
|
424
|
+
* Stringset fields are owned by getStringSetFromYjs (which expects the
|
|
425
|
+
* raw Y.Map / legacy plain-object shape — #561 / #601), so they pass
|
|
426
|
+
* through untouched. For any other field, a composite Yjs primitive
|
|
427
|
+
* (Y.Map / Y.Array / Y.Text) must never reach user code or the DB layer
|
|
428
|
+
* raw: normalize it to its plain-JS projection (toJSON / toString) and
|
|
429
|
+
* warn, rather than throwing. Scalars pass through unchanged.
|
|
430
|
+
*/
|
|
431
|
+
private normalizeNonStringSetValue;
|
|
397
432
|
private getValue;
|
|
398
433
|
private setValue;
|
|
399
434
|
get isDirty(): boolean;
|
|
@@ -754,6 +789,25 @@ interface SaveRequest {
|
|
|
754
789
|
ifNotExists?: boolean;
|
|
755
790
|
condition?: DocumentFilter;
|
|
756
791
|
upsertOn?: string;
|
|
792
|
+
/**
|
|
793
|
+
* Per-call auto-timestamp directives for schemaless writes.
|
|
794
|
+
*
|
|
795
|
+
* Each entry is a list of field names to stamp with `Date.now()`
|
|
796
|
+
* (milliseconds) at the corresponding lifecycle event:
|
|
797
|
+
*
|
|
798
|
+
* - `create` — field is stamped only when the record is new
|
|
799
|
+
* (no row exists yet for this `modelName` + `id`).
|
|
800
|
+
* - `update` — field is stamped on every save (create AND update).
|
|
801
|
+
*
|
|
802
|
+
* If the caller already provided an explicit value for the field
|
|
803
|
+
* in `data`, the explicit value wins and the stamp is skipped.
|
|
804
|
+
* Stamping happens BEFORE the `beforeSave` user hook runs, so user
|
|
805
|
+
* hooks may still overwrite the stamped value if they wish.
|
|
806
|
+
*/
|
|
807
|
+
autoStamps?: {
|
|
808
|
+
create?: string[];
|
|
809
|
+
update?: string[];
|
|
810
|
+
};
|
|
757
811
|
}
|
|
758
812
|
interface DeleteRequest {
|
|
759
813
|
modelName: string;
|
|
@@ -862,6 +916,17 @@ interface PatchRequest {
|
|
|
862
916
|
data: Record<string, any>;
|
|
863
917
|
stringSets?: Record<string, string[]>;
|
|
864
918
|
condition?: DocumentFilter;
|
|
919
|
+
/**
|
|
920
|
+
* Per-call auto-timestamp directives. A patch is always treated as
|
|
921
|
+
* an update (`isNew === false`), so only the `update` list is
|
|
922
|
+
* applied; the `create` list is ignored on patch.
|
|
923
|
+
*
|
|
924
|
+
* Explicit values in `data` win.
|
|
925
|
+
*/
|
|
926
|
+
autoStamps?: {
|
|
927
|
+
create?: string[];
|
|
928
|
+
update?: string[];
|
|
929
|
+
};
|
|
865
930
|
}
|
|
866
931
|
interface PatchResponse {
|
|
867
932
|
success: boolean;
|
|
@@ -877,6 +942,19 @@ interface BatchOperation {
|
|
|
877
942
|
ifNotExists?: boolean;
|
|
878
943
|
condition?: DocumentFilter;
|
|
879
944
|
upsertOn?: string;
|
|
945
|
+
/**
|
|
946
|
+
* Per-operation auto-timestamp directives. Same semantics as
|
|
947
|
+
* `SaveRequest.autoStamps` / `PatchRequest.autoStamps`. Applied
|
|
948
|
+
* BEFORE the user `beforeSave` hook runs, with explicit values
|
|
949
|
+
* in `data` always winning.
|
|
950
|
+
*
|
|
951
|
+
* For `op: "patch"`, only the `update` list applies (patches are
|
|
952
|
+
* always treated as updates).
|
|
953
|
+
*/
|
|
954
|
+
autoStamps?: {
|
|
955
|
+
create?: string[];
|
|
956
|
+
update?: string[];
|
|
957
|
+
};
|
|
880
958
|
}
|
|
881
959
|
interface BatchRequest {
|
|
882
960
|
operations: BatchOperation[];
|
|
@@ -1435,6 +1513,7 @@ interface DiscoveredField {
|
|
|
1435
1513
|
required?: boolean;
|
|
1436
1514
|
default?: string | number | boolean;
|
|
1437
1515
|
autoAssign?: boolean;
|
|
1516
|
+
autoStamp?: "create" | "update" | "both";
|
|
1438
1517
|
maxLength?: number;
|
|
1439
1518
|
maxCount?: number;
|
|
1440
1519
|
}
|
|
@@ -1541,6 +1620,11 @@ declare function loadSchemaFromTomlString(tomlString: string, options?: LoadSche
|
|
|
1541
1620
|
|
|
1542
1621
|
/**
|
|
1543
1622
|
* Infer a _meta_ type string from a JS runtime value.
|
|
1623
|
+
*
|
|
1624
|
+
* Only an all-`true` Y.Map (the stringset wire shape) is tagged as
|
|
1625
|
+
* `stringset` (issue #625) — a Y.Map carrying a composite payload, or a
|
|
1626
|
+
* Y.Array / Y.Text, is not a stringset and stays untyped (`null`) rather
|
|
1627
|
+
* than being mis-tagged.
|
|
1544
1628
|
*/
|
|
1545
1629
|
declare function inferFieldType(value: unknown): "string" | "number" | "boolean" | "stringset" | null;
|
|
1546
1630
|
/**
|
package/dist/cloudflare.d.ts
CHANGED
|
@@ -31,8 +31,32 @@ interface FieldOptions {
|
|
|
31
31
|
unique?: boolean;
|
|
32
32
|
default?: any | (() => any);
|
|
33
33
|
autoAssign?: boolean;
|
|
34
|
+
/**
|
|
35
|
+
* Auto-timestamp this field with `Date.now()` (milliseconds) on save.
|
|
36
|
+
*
|
|
37
|
+
* - `'create'` — stamp only on the first save (when `isNew === true`).
|
|
38
|
+
* - `'update'` — stamp on every save (create AND update).
|
|
39
|
+
* - `'both'` — stamp on every save (create AND update).
|
|
40
|
+
*
|
|
41
|
+
* If the caller passes an explicit value for the field in `data`, the
|
|
42
|
+
* explicit value wins and the stamp is skipped. The stamp is applied
|
|
43
|
+
* in `beforeSave` BEFORE any user-defined hooks, so user hooks may
|
|
44
|
+
* still overwrite the value if they wish (last writer wins).
|
|
45
|
+
*/
|
|
46
|
+
autoStamp?: "create" | "update" | "both";
|
|
34
47
|
maxLength?: number;
|
|
35
48
|
maxCount?: number;
|
|
49
|
+
/**
|
|
50
|
+
* Allowed-value set for a `string` field. When present, the codegen
|
|
51
|
+
* generators (database-type codegen + doc-model v2 codegen) emit a
|
|
52
|
+
* TypeScript string-literal union (`"a" | "b" | "c"`) instead of a bare
|
|
53
|
+
* `string` for this field.
|
|
54
|
+
*
|
|
55
|
+
* Advisory / codegen-only: this is a TS-emission hint. The runtime and the
|
|
56
|
+
* server do NOT enforce enum membership on write (see #843). Only valid on
|
|
57
|
+
* `string` fields, and must be a non-empty array of strings.
|
|
58
|
+
*/
|
|
59
|
+
enum?: string[];
|
|
36
60
|
}
|
|
37
61
|
interface UniqueConstraintConfig {
|
|
38
62
|
name: string;
|
|
@@ -394,6 +418,17 @@ declare class BaseModel implements StringSetChangeTracker {
|
|
|
394
418
|
private ensureLocalChanges;
|
|
395
419
|
private hasLocalChange;
|
|
396
420
|
private getFromYjs;
|
|
421
|
+
/**
|
|
422
|
+
* Defensive normalization for the read/sync boundary (issue #625).
|
|
423
|
+
*
|
|
424
|
+
* Stringset fields are owned by getStringSetFromYjs (which expects the
|
|
425
|
+
* raw Y.Map / legacy plain-object shape — #561 / #601), so they pass
|
|
426
|
+
* through untouched. For any other field, a composite Yjs primitive
|
|
427
|
+
* (Y.Map / Y.Array / Y.Text) must never reach user code or the DB layer
|
|
428
|
+
* raw: normalize it to its plain-JS projection (toJSON / toString) and
|
|
429
|
+
* warn, rather than throwing. Scalars pass through unchanged.
|
|
430
|
+
*/
|
|
431
|
+
private normalizeNonStringSetValue;
|
|
397
432
|
private getValue;
|
|
398
433
|
private setValue;
|
|
399
434
|
get isDirty(): boolean;
|
|
@@ -754,6 +789,25 @@ interface SaveRequest {
|
|
|
754
789
|
ifNotExists?: boolean;
|
|
755
790
|
condition?: DocumentFilter;
|
|
756
791
|
upsertOn?: string;
|
|
792
|
+
/**
|
|
793
|
+
* Per-call auto-timestamp directives for schemaless writes.
|
|
794
|
+
*
|
|
795
|
+
* Each entry is a list of field names to stamp with `Date.now()`
|
|
796
|
+
* (milliseconds) at the corresponding lifecycle event:
|
|
797
|
+
*
|
|
798
|
+
* - `create` — field is stamped only when the record is new
|
|
799
|
+
* (no row exists yet for this `modelName` + `id`).
|
|
800
|
+
* - `update` — field is stamped on every save (create AND update).
|
|
801
|
+
*
|
|
802
|
+
* If the caller already provided an explicit value for the field
|
|
803
|
+
* in `data`, the explicit value wins and the stamp is skipped.
|
|
804
|
+
* Stamping happens BEFORE the `beforeSave` user hook runs, so user
|
|
805
|
+
* hooks may still overwrite the stamped value if they wish.
|
|
806
|
+
*/
|
|
807
|
+
autoStamps?: {
|
|
808
|
+
create?: string[];
|
|
809
|
+
update?: string[];
|
|
810
|
+
};
|
|
757
811
|
}
|
|
758
812
|
interface DeleteRequest {
|
|
759
813
|
modelName: string;
|
|
@@ -862,6 +916,17 @@ interface PatchRequest {
|
|
|
862
916
|
data: Record<string, any>;
|
|
863
917
|
stringSets?: Record<string, string[]>;
|
|
864
918
|
condition?: DocumentFilter;
|
|
919
|
+
/**
|
|
920
|
+
* Per-call auto-timestamp directives. A patch is always treated as
|
|
921
|
+
* an update (`isNew === false`), so only the `update` list is
|
|
922
|
+
* applied; the `create` list is ignored on patch.
|
|
923
|
+
*
|
|
924
|
+
* Explicit values in `data` win.
|
|
925
|
+
*/
|
|
926
|
+
autoStamps?: {
|
|
927
|
+
create?: string[];
|
|
928
|
+
update?: string[];
|
|
929
|
+
};
|
|
865
930
|
}
|
|
866
931
|
interface PatchResponse {
|
|
867
932
|
success: boolean;
|
|
@@ -877,6 +942,19 @@ interface BatchOperation {
|
|
|
877
942
|
ifNotExists?: boolean;
|
|
878
943
|
condition?: DocumentFilter;
|
|
879
944
|
upsertOn?: string;
|
|
945
|
+
/**
|
|
946
|
+
* Per-operation auto-timestamp directives. Same semantics as
|
|
947
|
+
* `SaveRequest.autoStamps` / `PatchRequest.autoStamps`. Applied
|
|
948
|
+
* BEFORE the user `beforeSave` hook runs, with explicit values
|
|
949
|
+
* in `data` always winning.
|
|
950
|
+
*
|
|
951
|
+
* For `op: "patch"`, only the `update` list applies (patches are
|
|
952
|
+
* always treated as updates).
|
|
953
|
+
*/
|
|
954
|
+
autoStamps?: {
|
|
955
|
+
create?: string[];
|
|
956
|
+
update?: string[];
|
|
957
|
+
};
|
|
880
958
|
}
|
|
881
959
|
interface BatchRequest {
|
|
882
960
|
operations: BatchOperation[];
|
|
@@ -1435,6 +1513,7 @@ interface DiscoveredField {
|
|
|
1435
1513
|
required?: boolean;
|
|
1436
1514
|
default?: string | number | boolean;
|
|
1437
1515
|
autoAssign?: boolean;
|
|
1516
|
+
autoStamp?: "create" | "update" | "both";
|
|
1438
1517
|
maxLength?: number;
|
|
1439
1518
|
maxCount?: number;
|
|
1440
1519
|
}
|
|
@@ -1541,6 +1620,11 @@ declare function loadSchemaFromTomlString(tomlString: string, options?: LoadSche
|
|
|
1541
1620
|
|
|
1542
1621
|
/**
|
|
1543
1622
|
* Infer a _meta_ type string from a JS runtime value.
|
|
1623
|
+
*
|
|
1624
|
+
* Only an all-`true` Y.Map (the stringset wire shape) is tagged as
|
|
1625
|
+
* `stringset` (issue #625) — a Y.Map carrying a composite payload, or a
|
|
1626
|
+
* Y.Array / Y.Text, is not a stringset and stays untyped (`null`) rather
|
|
1627
|
+
* than being mis-tagged.
|
|
1544
1628
|
*/
|
|
1545
1629
|
declare function inferFieldType(value: unknown): "string" | "number" | "boolean" | "stringset" | null;
|
|
1546
1630
|
/**
|