@elytracms/studio 0.0.98 → 0.0.100
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/convex/cms.ts +29 -2
- package/convex/ingest.test.ts +84 -7
- package/convex/ingest.ts +110 -31
- package/convex/path-segment-refusal.test.ts +201 -0
- package/convex/records-column-transform.convex.test.ts +149 -0
- package/convex/records-one-write.convex.test.ts +218 -0
- package/convex/schema.ts +1 -1
- package/convex/version-kind-stamping.test.ts +118 -0
- package/dist/addon.d.ts +5 -3
- package/dist/addon.js +4 -3
- package/dist/addon.js.map +1 -1
- package/dist/components/app-views/default-modules.d.ts +10 -3
- package/dist/components/app-views/default-modules.js +5 -1
- package/dist/components/app-views/default-modules.js.map +1 -1
- package/dist/components/app-views/module.d.ts +15 -8
- package/dist/components/app-views/module.js.map +1 -1
- package/dist/components/builder/reuse-context.d.ts +29 -1
- package/dist/components/builder/reuse-context.js +58 -12
- package/dist/components/builder/reuse-context.js.map +1 -1
- package/dist/components/cms/cms-store.d.ts +3 -2
- package/dist/components/cms/cms-store.js +8 -2
- package/dist/components/cms/cms-store.js.map +1 -1
- package/dist/components/cms/field-control.js +15 -5
- package/dist/components/cms/field-control.js.map +1 -1
- package/dist/components/cms/use-document-draft.d.ts +4 -1
- package/dist/components/cms/use-document-draft.js +4 -1
- package/dist/components/cms/use-document-draft.js.map +1 -1
- package/dist/components/cms/use-publish-controller.d.ts +7 -0
- package/dist/components/cms/use-publish-controller.js +17 -3
- package/dist/components/cms/use-publish-controller.js.map +1 -1
- package/dist/components/cms/versions-pane.js +8 -5
- package/dist/components/cms/versions-pane.js.map +1 -1
- package/dist/components/delivery-health-banner.d.ts +34 -16
- package/dist/components/delivery-health-banner.js +59 -27
- package/dist/components/delivery-health-banner.js.map +1 -1
- package/dist/components/issue-severity.d.ts +3 -2
- package/dist/components/issue-severity.js.map +1 -1
- package/dist/components/nav-issues-registry.d.ts +39 -24
- package/dist/components/nav-issues-registry.js +50 -21
- package/dist/components/nav-issues-registry.js.map +1 -1
- package/dist/components/nav-issues.d.ts +23 -50
- package/dist/components/nav-issues.js +31 -59
- package/dist/components/nav-issues.js.map +1 -1
- package/dist/components/notifications/notice-center.d.ts +61 -0
- package/dist/components/notifications/notice-center.js +119 -0
- package/dist/components/notifications/notice-center.js.map +1 -0
- package/dist/components/notifications/notice-model.d.ts +137 -0
- package/dist/components/notifications/notice-model.js +107 -0
- package/dist/components/notifications/notice-model.js.map +1 -0
- package/dist/components/notifications/outcome-notice.d.ts +16 -0
- package/dist/components/notifications/outcome-notice.js +34 -0
- package/dist/components/notifications/outcome-notice.js.map +1 -0
- package/dist/components/notifications/shell-findings.d.ts +36 -0
- package/dist/components/notifications/shell-findings.js +140 -0
- package/dist/components/notifications/shell-findings.js.map +1 -0
- package/dist/components/rail-view-source.js +5 -0
- package/dist/components/rail-view-source.js.map +1 -1
- package/dist/components/records/record-rail.d.ts +10 -2
- package/dist/components/records/record-rail.js +85 -4
- package/dist/components/records/record-rail.js.map +1 -1
- package/dist/components/section-nav.d.ts +15 -3
- package/dist/components/section-nav.js +39 -147
- package/dist/components/section-nav.js.map +1 -1
- package/dist/components/studio-project-screen.js.map +1 -1
- package/dist/components/studio-shell.js +8 -3
- package/dist/components/studio-shell.js.map +1 -1
- package/dist/components/use-narrow-viewport.d.ts +11 -0
- package/dist/components/use-narrow-viewport.js +25 -0
- package/dist/components/use-narrow-viewport.js.map +1 -0
- package/dist/components/users/role-policy-drift-issues.d.ts +14 -7
- package/dist/components/users/role-policy-drift-issues.js +17 -8
- package/dist/components/users/role-policy-drift-issues.js.map +1 -1
- package/dist/lib/collection-view/collection-view-source.d.ts +4 -2
- package/dist/lib/collection-view/collection-view-source.js.map +1 -1
- package/dist/lib/engine-version.d.ts +1 -1
- package/dist/lib/engine-version.js +1 -1
- package/dist/lib/engine-version.js.map +1 -1
- package/dist/lib/i18n/messages.de.js +22 -3
- package/dist/lib/i18n/messages.de.js.map +1 -1
- package/dist/lib/i18n/messages.en.d.ts +20 -2
- package/dist/lib/i18n/messages.en.js +25 -4
- package/dist/lib/i18n/messages.en.js.map +1 -1
- package/dist/lib/notices/toast-store.d.ts +61 -0
- package/dist/lib/notices/toast-store.js +116 -0
- package/dist/lib/notices/toast-store.js.map +1 -0
- package/dist/lib/operations/cms-persistence.d.ts +24 -4
- package/dist/lib/operations/cms-persistence.js +411 -80
- package/dist/lib/operations/cms-persistence.js.map +1 -1
- package/dist/lib/persistence/convex-adapter.d.ts +2 -0
- package/dist/lib/persistence/convex-adapter.js +127 -31
- package/dist/lib/persistence/convex-adapter.js.map +1 -1
- package/dist/ui/toast.d.ts +24 -16
- package/dist/ui/toast.js +23 -32
- package/dist/ui/toast.js.map +1 -1
- package/package.json +3 -3
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { ConvexError } from 'convex/values';
|
|
1
|
+
import { ConvexError, convexToJson } from 'convex/values';
|
|
2
2
|
import { DRAFT_VARIANT, documentKey, documentSchema } from '@elytracms/core/cms-core';
|
|
3
|
-
import { PRESENCE_TTL_MS, RecordNotFoundError, PersistenceValidationError, RevisionPreconditionError, StrictReferencePreconditionError, presenceInputSchema, assetCreateDefaults, assetRecordFromRow, compareReferenceEntries, persistenceIssueSchema, presentWriteKeys, recordEntryFromWire, validateForPersistence, versionNotPinnableError, } from '@elytracms/core/persistence';
|
|
3
|
+
import { BackendUnavailableError, PRESENCE_TTL_MS, RecordNotFoundError, PersistenceValidationError, RevisionPreconditionError, StrictReferencePreconditionError, presenceInputSchema, assetCreateDefaults, assetRecordFromRow, compareReferenceEntries, isPersistenceError, persistenceIssueSchema, presentWriteKeys, recordEntryFromWire, rejectedWriteMessage, validateForPersistence, versionNotPinnableError, } from '@elytracms/core/persistence';
|
|
4
4
|
import { normalizeInviteEmail } from '@elytracms/core/persistence';
|
|
5
5
|
import { createUuidIdFactory } from '@elytracms/core/determinism';
|
|
6
6
|
import { SUBSCRIPTION_UNAUTHENTICATED_MESSAGE, queryRef, mutationRef } from './convex-client.js';
|
|
@@ -17,6 +17,104 @@ function newId(prefix) {
|
|
|
17
17
|
mintUuid ??= createUuidIdFactory();
|
|
18
18
|
return `${prefix}_${mintUuid()}`;
|
|
19
19
|
}
|
|
20
|
+
/**
|
|
21
|
+
* EC-686 — what a Convex call's promise rejects with, classified by WHO failed (the binding
|
|
22
|
+
* rule, wave-0100). `convex/browser` (1.42.1, `http_client.js`) rejects with a raw
|
|
23
|
+
* `TypeError` for a fetch rejection, with `Error(<body text>)` for any non-OK status (the
|
|
24
|
+
* status discarded), and with the function's `Error(message)` or `ConvexError` for a
|
|
25
|
+
* function failure: none of them says whether the store JUDGED the write. So a validation
|
|
26
|
+
* verdict the engine throws on purpose and this adapter maps ({@link asRailWriteError}) is
|
|
27
|
+
* that error, and anything else is a {@link BackendUnavailableError} carrying the original
|
|
28
|
+
* message (an expired session is read from it — `isSessionExpiredError`): the session
|
|
29
|
+
* keeps the write and re-sends it (`keepsFailedWrite`, EC-674). An `unknown` is then only
|
|
30
|
+
* ever a failure in THIS process, which the same input repeats.
|
|
31
|
+
*/
|
|
32
|
+
function backendFailure(error) {
|
|
33
|
+
const mapped = asRailWriteError(error);
|
|
34
|
+
if (isPersistenceError(mapped))
|
|
35
|
+
return mapped;
|
|
36
|
+
return new BackendUnavailableError(error instanceof Error ? error.message : String(error), { cause: error });
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* EC-686 — the client's calls with their promise classified ({@link backendFailure}), the
|
|
40
|
+
* one seam every repository calls through: never a whole adapter method, so a crash of
|
|
41
|
+
* this process before or after the call (a parse, a row mapping) stays `unknown`. The
|
|
42
|
+
* arguments are serialised for the wire here first (`convexToJson`, the client's own
|
|
43
|
+
* conversion): a value Convex cannot carry (a `$`-prefixed or non-ASCII key) fails in this
|
|
44
|
+
* process, never inside the call — it is the input, not the backend. The shared client
|
|
45
|
+
* itself is never patched (`lib/auth/**` reads its errors as they are).
|
|
46
|
+
*/
|
|
47
|
+
function judgedCalls(client) {
|
|
48
|
+
const judged = (args, call) => {
|
|
49
|
+
convexToJson(args);
|
|
50
|
+
return call().catch((error) => {
|
|
51
|
+
throw backendFailure(error);
|
|
52
|
+
});
|
|
53
|
+
};
|
|
54
|
+
return {
|
|
55
|
+
query: ((ref, args = {}) => judged(args, () => client.query(ref, args))),
|
|
56
|
+
mutation: ((ref, args = {}) => judged(args, () => client.mutation(ref, args))),
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
/** Issue code: a record write holding a value the Convex wire cannot carry (EC-702). */
|
|
60
|
+
export const UNCARRIABLE_VALUE = 'uncarriable-value';
|
|
61
|
+
/** Why Convex cannot carry `value` (its own conversion's reason, without the echoed object), or `undefined`. */
|
|
62
|
+
function wireRefusal(value) {
|
|
63
|
+
try {
|
|
64
|
+
convexToJson(value);
|
|
65
|
+
return undefined;
|
|
66
|
+
}
|
|
67
|
+
catch (error) {
|
|
68
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
69
|
+
return message.split(' (present at path')[0].split(' To learn about')[0].trim();
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* EC-702 — the located refusals of a record write's values the Convex wire cannot carry: a
|
|
74
|
+
* `Date`, a `Map`, a class instance, an `undefined` in a list, a `$`-prefixed or non-ASCII key.
|
|
75
|
+
* The judge is Convex's own conversion (`convexToJson`, the one {@link judgedCalls} runs on every
|
|
76
|
+
* call's arguments), run per column so the refusal names the column — and, below it, the deepest
|
|
77
|
+
* value or key that fails. The in-memory store refuses the same write located, by the column's
|
|
78
|
+
* own schema; without this the Convex path threw an unlocated `Error` in this process
|
|
79
|
+
* (`unknown`). Before EC-702 the operations' step 1 located it, by running the column schemas a
|
|
80
|
+
* second time.
|
|
81
|
+
*/
|
|
82
|
+
function uncarriableIssues(write) {
|
|
83
|
+
const issues = [];
|
|
84
|
+
for (const [column, value] of Object.entries(write)) {
|
|
85
|
+
const found = uncarriableAt(value, [column]);
|
|
86
|
+
if (found) {
|
|
87
|
+
issues.push({
|
|
88
|
+
code: UNCARRIABLE_VALUE,
|
|
89
|
+
message: `The Convex store cannot carry this value: ${found.reason}`,
|
|
90
|
+
path: found.path,
|
|
91
|
+
domain: 'persistence',
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return issues;
|
|
96
|
+
}
|
|
97
|
+
function uncarriableAt(value, path) {
|
|
98
|
+
const reason = wireRefusal(value);
|
|
99
|
+
if (reason === undefined)
|
|
100
|
+
return undefined;
|
|
101
|
+
const plain = typeof value === 'object' &&
|
|
102
|
+
value !== null &&
|
|
103
|
+
(Array.isArray(value) || Object.getPrototypeOf(value) === Object.prototype || Object.getPrototypeOf(value) === null);
|
|
104
|
+
if (plain) {
|
|
105
|
+
for (const [key, item] of Object.entries(value)) {
|
|
106
|
+
const segment = Array.isArray(value) ? Number(key) : key;
|
|
107
|
+
// A key Convex refuses is the refusal itself, wherever its value is fine.
|
|
108
|
+
const keyReason = Array.isArray(value) ? undefined : wireRefusal({ [key]: null });
|
|
109
|
+
if (keyReason !== undefined)
|
|
110
|
+
return { path: [...path, segment], reason: keyReason };
|
|
111
|
+
const inner = uncarriableAt(item, [...path, segment]);
|
|
112
|
+
if (inner)
|
|
113
|
+
return inner;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
return { path, reason };
|
|
117
|
+
}
|
|
20
118
|
/**
|
|
21
119
|
* Row → contract record. `projectId` is the caller's (the row has none): before
|
|
22
120
|
* EC-577 this copied the row's nonexistent `projectId`, so every connected
|
|
@@ -411,11 +509,12 @@ class ConvexCmsRepository {
|
|
|
411
509
|
}
|
|
412
510
|
}
|
|
413
511
|
/**
|
|
414
|
-
* A
|
|
415
|
-
* invalid inside the transaction and threw a `validation`
|
|
416
|
-
* back onto the provider-neutral {@link PersistenceValidationError}
|
|
417
|
-
* adapter throws for the same write. `ConvexError` data survives a production
|
|
418
|
-
* deployment's error redaction, unlike a plain thrown message.
|
|
512
|
+
* A write the handler rejected (EC-577): `resolveRecordWrite` (a rail or record write) or
|
|
513
|
+
* the asset upsert found the write invalid inside the transaction and threw a `validation`
|
|
514
|
+
* `ConvexError` — mapped back onto the provider-neutral {@link PersistenceValidationError}
|
|
515
|
+
* the in-memory adapter throws for the same write. `ConvexError` data survives a production
|
|
516
|
+
* deployment's error redaction, unlike a plain thrown message. EC-686: read by
|
|
517
|
+
* {@link backendFailure} for every call, so no call site maps it itself.
|
|
419
518
|
*/
|
|
420
519
|
function asRailWriteError(error) {
|
|
421
520
|
if (!(error instanceof ConvexError))
|
|
@@ -440,16 +539,12 @@ class ConvexAssetRepository {
|
|
|
440
539
|
// EC-577: send the write as written — absent keys stay absent (keep), `null`
|
|
441
540
|
// crosses the wire as the clear. The handler resolves it against the stored
|
|
442
541
|
// row inside the transaction; the create defaults ride along for a create.
|
|
443
|
-
const doc = (await this.client
|
|
444
|
-
.mutation(mutationRef('assets:upsert'), {
|
|
542
|
+
const doc = (await this.client.mutation(mutationRef('assets:upsert'), {
|
|
445
543
|
assetId: id,
|
|
446
544
|
...presentWriteKeys(write),
|
|
447
545
|
defaults: assetCreateDefaults(projectId, id),
|
|
448
546
|
createdAt: now,
|
|
449
547
|
updatedAt: now,
|
|
450
|
-
})
|
|
451
|
-
.catch((error) => {
|
|
452
|
-
throw asRailWriteError(error);
|
|
453
548
|
}));
|
|
454
549
|
return assetRecordFromRow(doc, projectId);
|
|
455
550
|
}
|
|
@@ -826,11 +921,7 @@ class ConvexRecordRepository {
|
|
|
826
921
|
return `${this.kind.module}:${fn}`;
|
|
827
922
|
}
|
|
828
923
|
async list(projectId, query = {}) {
|
|
829
|
-
const rows = (await this.client
|
|
830
|
-
.query(queryRef(this.ref('list')), presentWriteKeys({ ...query }))
|
|
831
|
-
.catch((error) => {
|
|
832
|
-
throw asRailWriteError(error);
|
|
833
|
-
}));
|
|
924
|
+
const rows = (await this.client.query(queryRef(this.ref('list')), presentWriteKeys({ ...query })));
|
|
834
925
|
return rows.map((row) => recordEntryFromWire(this.kind, row, projectId));
|
|
835
926
|
}
|
|
836
927
|
async get(projectId, id) {
|
|
@@ -849,15 +940,17 @@ class ConvexRecordRepository {
|
|
|
849
940
|
const now = this.clock();
|
|
850
941
|
const { id: givenId, ...write } = input;
|
|
851
942
|
const id = givenId ?? newId(this.kind.idPrefix);
|
|
852
|
-
const
|
|
853
|
-
|
|
943
|
+
const sent = presentWriteKeys(write);
|
|
944
|
+
// EC-702: a value the wire cannot carry is the write's located refusal, as in memory.
|
|
945
|
+
const uncarriable = uncarriableIssues(sent);
|
|
946
|
+
if (uncarriable.length > 0) {
|
|
947
|
+
throw new PersistenceValidationError(rejectedWriteMessage(this.kind.kind, id, uncarriable), uncarriable);
|
|
948
|
+
}
|
|
949
|
+
const row = (await this.client.mutation(mutationRef(this.ref('save')), {
|
|
854
950
|
[this.kind.idKey]: id,
|
|
855
|
-
...
|
|
951
|
+
...sent,
|
|
856
952
|
createdAt: now,
|
|
857
953
|
updatedAt: now,
|
|
858
|
-
})
|
|
859
|
-
.catch((error) => {
|
|
860
|
-
throw asRailWriteError(error);
|
|
861
954
|
}));
|
|
862
955
|
return recordEntryFromWire(this.kind, row, projectId);
|
|
863
956
|
}
|
|
@@ -931,23 +1024,26 @@ export class ConvexPersistenceAdapter {
|
|
|
931
1024
|
*/
|
|
932
1025
|
constructor(client, projectId, options = {}) {
|
|
933
1026
|
this.client = client;
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
this.
|
|
938
|
-
this.
|
|
939
|
-
this.
|
|
1027
|
+
// EC-686: every repository calls through the classified calls; the probe below and the
|
|
1028
|
+
// blob boundary (its own failure handling) keep the client as it is.
|
|
1029
|
+
const calls = judgedCalls(client);
|
|
1030
|
+
this.cms = new ConvexCmsRepository(calls, options.reactive);
|
|
1031
|
+
this.assets = new ConvexAssetRepository(calls);
|
|
1032
|
+
this.references = new ConvexReferenceIndexRepository(calls);
|
|
1033
|
+
this.members = new ConvexMembersRepository(calls, projectId);
|
|
1034
|
+
this.users = new ConvexUserRepository(calls);
|
|
1035
|
+
this.invites = new ConvexInviteRepository(calls);
|
|
940
1036
|
const recordRepositories = new Map();
|
|
941
1037
|
this.records = (kind) => {
|
|
942
1038
|
let repository = recordRepositories.get(kind);
|
|
943
1039
|
if (!repository) {
|
|
944
|
-
repository = new ConvexRecordRepository(
|
|
1040
|
+
repository = new ConvexRecordRepository(calls, kind);
|
|
945
1041
|
recordRepositories.set(kind, repository);
|
|
946
1042
|
}
|
|
947
1043
|
return repository;
|
|
948
1044
|
};
|
|
949
1045
|
this.blobs = new ConvexBlobStorage(client);
|
|
950
|
-
this.delivery = new ConvexDeliveryRepository(
|
|
1046
|
+
this.delivery = new ConvexDeliveryRepository(calls);
|
|
951
1047
|
}
|
|
952
1048
|
async status() {
|
|
953
1049
|
try {
|