@pylonsync/sdk 0.3.268 → 0.3.270
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/package.json +1 -1
- package/src/index.ts +53 -123
- package/src/studio.ts +1 -1
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -43,6 +43,16 @@ export type CrdtAnnotation =
|
|
|
43
43
|
| "movable-list"
|
|
44
44
|
| "tree";
|
|
45
45
|
|
|
46
|
+
/**
|
|
47
|
+
* Insert-time default marker stored on a field definition. Apps never
|
|
48
|
+
* construct these directly — they come from `.default(v)` / `.defaultNow()`
|
|
49
|
+
* / `.owner()` on a field builder. The runtime + codegen read them.
|
|
50
|
+
*/
|
|
51
|
+
export type DefaultMarker =
|
|
52
|
+
| { kind: "value"; value: unknown }
|
|
53
|
+
| { kind: "now" }
|
|
54
|
+
| { kind: "owner" };
|
|
55
|
+
|
|
46
56
|
export interface FieldDefinition {
|
|
47
57
|
type: FieldType;
|
|
48
58
|
optional: boolean;
|
|
@@ -119,6 +129,16 @@ export interface FieldDefinition {
|
|
|
119
129
|
* pipeline upgrades them to ciphertext.
|
|
120
130
|
*/
|
|
121
131
|
encrypted?: boolean;
|
|
132
|
+
/**
|
|
133
|
+
* Insert-time default. Set via `.default(value)` / `.defaultNow()` /
|
|
134
|
+
* `.owner()` on the field builder; recorded for the runtime + codegen.
|
|
135
|
+
*/
|
|
136
|
+
default?: DefaultMarker;
|
|
137
|
+
/**
|
|
138
|
+
* Allowed values for `field.enum([...])` — recorded so codegen emits a
|
|
139
|
+
* precise literal-union type instead of a wide `string`.
|
|
140
|
+
*/
|
|
141
|
+
enumValues?: readonly string[];
|
|
122
142
|
}
|
|
123
143
|
|
|
124
144
|
interface FieldBuilder {
|
|
@@ -194,6 +214,16 @@ interface FieldBuilder {
|
|
|
194
214
|
* stays unspoofable.
|
|
195
215
|
*/
|
|
196
216
|
owner(): FieldBuilder;
|
|
217
|
+
/**
|
|
218
|
+
* Set a static insert-time default, recorded for the runtime +
|
|
219
|
+
* codegen. Example: `enabled: field.bool().default(true)`.
|
|
220
|
+
*/
|
|
221
|
+
default(value: unknown): FieldBuilder;
|
|
222
|
+
/**
|
|
223
|
+
* Default a datetime field to insert-time `now()`. Example:
|
|
224
|
+
* `createdAt: field.datetime().defaultNow()`.
|
|
225
|
+
*/
|
|
226
|
+
defaultNow(): FieldBuilder;
|
|
197
227
|
}
|
|
198
228
|
|
|
199
229
|
function createFieldBuilder(type: FieldType): FieldBuilder {
|
|
@@ -224,11 +254,13 @@ function buildField(def: FieldDefinition): FieldBuilder {
|
|
|
224
254
|
owner() {
|
|
225
255
|
// Dynamic default filled by the auth-aware pipeline; also lock
|
|
226
256
|
// the field on update so ownership can't be reassigned via PATCH.
|
|
227
|
-
return buildField({
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
257
|
+
return buildField({ ...def, readonly: true, default: { kind: "owner" } });
|
|
258
|
+
},
|
|
259
|
+
default(value: unknown) {
|
|
260
|
+
return buildField({ ...def, default: { kind: "value", value } });
|
|
261
|
+
},
|
|
262
|
+
defaultNow() {
|
|
263
|
+
return buildField({ ...def, default: { kind: "now" } });
|
|
232
264
|
},
|
|
233
265
|
};
|
|
234
266
|
}
|
|
@@ -250,6 +282,18 @@ export const field = {
|
|
|
250
282
|
datetime: () => createFieldBuilder("datetime"),
|
|
251
283
|
richtext: () => createFieldBuilder("richtext"),
|
|
252
284
|
id: (target: string) => createFieldBuilder(`id(${target})`),
|
|
285
|
+
/**
|
|
286
|
+
* `field.enum(["pending", "paid", "failed"])` — stored as a string with
|
|
287
|
+
* allowed-values metadata so codegen emits a precise literal union
|
|
288
|
+
* (`"pending" | "paid" | "failed"`) instead of a wide `string`.
|
|
289
|
+
*/
|
|
290
|
+
enum: (values: readonly string[]) =>
|
|
291
|
+
buildField({
|
|
292
|
+
type: "string",
|
|
293
|
+
optional: false,
|
|
294
|
+
unique: false,
|
|
295
|
+
enumValues: values,
|
|
296
|
+
}),
|
|
253
297
|
};
|
|
254
298
|
|
|
255
299
|
// ---------------------------------------------------------------------------
|
|
@@ -1506,124 +1550,10 @@ export const audit: Behavior = {
|
|
|
1506
1550
|
},
|
|
1507
1551
|
};
|
|
1508
1552
|
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
* the runtime to read.
|
|
1514
|
-
*/
|
|
1515
|
-
type DefaultMarker =
|
|
1516
|
-
| { kind: "value"; value: unknown }
|
|
1517
|
-
| { kind: "now" }
|
|
1518
|
-
| { kind: "owner" };
|
|
1519
|
-
|
|
1520
|
-
/**
|
|
1521
|
-
* Augment FieldBuilder with the new `default()` / `defaultNow()`
|
|
1522
|
-
* chainables. Runtime support for actually filling these values on
|
|
1523
|
-
* insert lands as part of v0.4.1; until then the markers are
|
|
1524
|
-
* recorded in the manifest for tooling + the codegen layer.
|
|
1525
|
-
*/
|
|
1526
|
-
declare module "./index" {
|
|
1527
|
-
// (Empty — the `default*` methods are added at runtime via the
|
|
1528
|
-
// patched buildField below. Apps see them via the FieldBuilder
|
|
1529
|
-
// surface declared above.)
|
|
1530
|
-
}
|
|
1531
|
-
|
|
1532
|
-
// Field builder with chainable `.default()` / `.defaultNow()`. All
|
|
1533
|
-
// other chainables (`optional`, `unique`, `crdt`, `serverOnly`,
|
|
1534
|
-
// `readonly`) are reimplemented here to return another
|
|
1535
|
-
// `buildFieldWithDefaults` — without this, calling `.optional()`
|
|
1536
|
-
// after `.default()` would drop the default markers off the chain
|
|
1537
|
-
// (codex Wave-3 review: the previous `{ ...base, default, defaultNow }`
|
|
1538
|
-
// pattern delegated optional/unique to the original buildField which
|
|
1539
|
-
// returned a builder lacking `.default()`). Recursion through the
|
|
1540
|
-
// same constructor keeps the surface stable regardless of chain
|
|
1541
|
-
// order.
|
|
1542
|
-
function buildFieldWithDefaults(
|
|
1543
|
-
def: FieldDefinition & {
|
|
1544
|
-
default?: DefaultMarker;
|
|
1545
|
-
enumValues?: readonly string[];
|
|
1546
|
-
},
|
|
1547
|
-
): FieldBuilder & {
|
|
1548
|
-
default(value: unknown): ReturnType<typeof buildFieldWithDefaults>;
|
|
1549
|
-
defaultNow(): ReturnType<typeof buildFieldWithDefaults>;
|
|
1550
|
-
owner(): ReturnType<typeof buildFieldWithDefaults>;
|
|
1551
|
-
} {
|
|
1552
|
-
return {
|
|
1553
|
-
_def: def,
|
|
1554
|
-
optional() {
|
|
1555
|
-
return buildFieldWithDefaults({ ...def, optional: true });
|
|
1556
|
-
},
|
|
1557
|
-
unique() {
|
|
1558
|
-
return buildFieldWithDefaults({ ...def, unique: true });
|
|
1559
|
-
},
|
|
1560
|
-
crdt(annotation) {
|
|
1561
|
-
return buildFieldWithDefaults({ ...def, crdt: annotation });
|
|
1562
|
-
},
|
|
1563
|
-
serverOnly() {
|
|
1564
|
-
return buildFieldWithDefaults({ ...def, serverOnly: true });
|
|
1565
|
-
},
|
|
1566
|
-
readonly() {
|
|
1567
|
-
return buildFieldWithDefaults({ ...def, readonly: true });
|
|
1568
|
-
},
|
|
1569
|
-
encrypted() {
|
|
1570
|
-
return buildFieldWithDefaults({ ...def, encrypted: true });
|
|
1571
|
-
},
|
|
1572
|
-
default(value: unknown) {
|
|
1573
|
-
return buildFieldWithDefaults({
|
|
1574
|
-
...def,
|
|
1575
|
-
default: { kind: "value", value },
|
|
1576
|
-
});
|
|
1577
|
-
},
|
|
1578
|
-
defaultNow() {
|
|
1579
|
-
return buildFieldWithDefaults({ ...def, default: { kind: "now" } });
|
|
1580
|
-
},
|
|
1581
|
-
owner() {
|
|
1582
|
-
// Server-stamped owner: a dynamic default the auth-aware
|
|
1583
|
-
// pipeline fills + enforces, plus `readonly` so the owner can't
|
|
1584
|
-
// be reassigned via a later PATCH. See FieldBuilder.owner().
|
|
1585
|
-
return buildFieldWithDefaults({
|
|
1586
|
-
...def,
|
|
1587
|
-
readonly: true,
|
|
1588
|
-
default: { kind: "owner" },
|
|
1589
|
-
});
|
|
1590
|
-
},
|
|
1591
|
-
};
|
|
1592
|
-
}
|
|
1593
|
-
// Re-export `field` with the patched builder so callers picking up
|
|
1594
|
-
// the new SDK get the chainables transparently. The old `field`
|
|
1595
|
-
// surface still works — `.default()` / `.defaultNow()` are additive.
|
|
1596
|
-
// We intentionally re-export from the same name so existing imports
|
|
1597
|
-
// (`import { field } from "@pylonsync/sdk"`) keep working AND gain
|
|
1598
|
-
// the new methods without a code change.
|
|
1599
|
-
Object.assign(field, {
|
|
1600
|
-
string: () => buildFieldWithDefaults({ type: "string", optional: false, unique: false }),
|
|
1601
|
-
int: () => buildFieldWithDefaults({ type: "int", optional: false, unique: false }),
|
|
1602
|
-
float: () => buildFieldWithDefaults({ type: "float", optional: false, unique: false }),
|
|
1603
|
-
number: () => buildFieldWithDefaults({ type: "float", optional: false, unique: false }),
|
|
1604
|
-
bool: () => buildFieldWithDefaults({ type: "bool", optional: false, unique: false }),
|
|
1605
|
-
boolean: () => buildFieldWithDefaults({ type: "bool", optional: false, unique: false }),
|
|
1606
|
-
datetime: () => buildFieldWithDefaults({ type: "datetime", optional: false, unique: false }),
|
|
1607
|
-
richtext: () => buildFieldWithDefaults({ type: "richtext", optional: false, unique: false }),
|
|
1608
|
-
id: (target: string) => buildFieldWithDefaults({ type: `id(${target})` as FieldType, optional: false, unique: false }),
|
|
1609
|
-
/**
|
|
1610
|
-
* `field.enum(["pending", "paid", "failed"])` — stored as a string
|
|
1611
|
-
* with allowed-values metadata. Runtime enforcement (CHECK
|
|
1612
|
-
* constraint or insert-time validation) lands in a follow-up
|
|
1613
|
-
* patch; for now the values flow through to codegen so the
|
|
1614
|
-
* generated client gets a precise `"pending" | "paid" | "failed"`
|
|
1615
|
-
* literal-union type instead of a wide `string`.
|
|
1616
|
-
*/
|
|
1617
|
-
enum(values: readonly string[]) {
|
|
1618
|
-
const def: FieldDefinition & { enumValues?: readonly string[] } = {
|
|
1619
|
-
type: "string",
|
|
1620
|
-
optional: false,
|
|
1621
|
-
unique: false,
|
|
1622
|
-
enumValues: values,
|
|
1623
|
-
};
|
|
1624
|
-
return buildFieldWithDefaults(def);
|
|
1625
|
-
},
|
|
1626
|
-
});
|
|
1553
|
+
// `field` builders (`default` / `defaultNow` / `owner` / `enum`) are all
|
|
1554
|
+
// implemented directly on `buildField` above and typed on the FieldBuilder
|
|
1555
|
+
// interface, so `field.datetime().defaultNow()` and `field.enum([...])`
|
|
1556
|
+
// are real, statically-typed chainables — no runtime augmentation needed.
|
|
1627
1557
|
|
|
1628
1558
|
/** Variadic index helper — `e.idx("customer", "createdAt")` reads
|
|
1629
1559
|
* better than the options-object form for the common case. */
|
package/src/studio.ts
CHANGED
|
@@ -360,7 +360,7 @@ export interface StudioConfig {
|
|
|
360
360
|
* the framework's "access denied" page (no point sending them back
|
|
361
361
|
* to a login they're already past).
|
|
362
362
|
*
|
|
363
|
-
* Example: `loginUrl: "/login"` —
|
|
363
|
+
* Example: `loginUrl: "/login"` — www.pylonsync.com handles `/login`
|
|
364
364
|
* at the dashboard, and the user's existing session cookie lifts
|
|
365
365
|
* them to admin via `auth.user.adminField` on the way back.
|
|
366
366
|
*/
|