create-substrat 0.7.3 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.js +5 -5
- package/package.json +1 -1
- package/template/.substrat/playbook.md +15 -0
- package/template/src/routes.ts +15 -12
package/index.js
CHANGED
|
@@ -36,11 +36,11 @@ const TEMPLATE = join(HERE, 'template');
|
|
|
36
36
|
// The runtime packages release together off one version line (the changesets `fixed`
|
|
37
37
|
// group), so one constant is right for all of them. Engines do NOT share a line —
|
|
38
38
|
// each versions on its own, so one pin per engine, deliberately.
|
|
39
|
-
const SUBSTRAT = '^0.
|
|
40
|
-
const ENGINE_WORKORDER = '^0.
|
|
41
|
-
const ENGINE_INVOICING = '^0.9.
|
|
42
|
-
const BOUNDARY_LINT = '^0.
|
|
43
|
-
const DEV_ISSUER = '^0.1.
|
|
39
|
+
const SUBSTRAT = '^0.89.0';
|
|
40
|
+
const ENGINE_WORKORDER = '^0.9.0';
|
|
41
|
+
const ENGINE_INVOICING = '^0.9.5';
|
|
42
|
+
const BOUNDARY_LINT = '^0.2.0';
|
|
43
|
+
const DEV_ISSUER = '^0.1.3';
|
|
44
44
|
|
|
45
45
|
const DOCS = 'https://substrat.net';
|
|
46
46
|
|
package/package.json
CHANGED
|
@@ -363,6 +363,21 @@ operations + the `ModuleRegistration`. Keep the split — the linter and tests e
|
|
|
363
363
|
`completeWorkOrder`. One transaction, invariants intact.
|
|
364
364
|
- Portal listing: iterate and `ctx.check(perm, entityRef)` **per entity** — a proof walk,
|
|
365
365
|
not UI filtering.
|
|
366
|
+
- **An entity's history is `readTimeline(ctx, entity, input)` from `@substrat-run/kernel`** —
|
|
367
|
+
not a `SELECT` against `_substrat_outbox`. Reading the spine is allowed (writes to
|
|
368
|
+
`_substrat_*` are not); hand-writing the query is what goes wrong. It returns
|
|
369
|
+
`{ entries, nextCursor }` with `{ id, type, occurredAt, actor }` per entry, and it exists
|
|
370
|
+
to close two traps: `actor` is stored as JSON over a union — a principal is `"01J…"`
|
|
371
|
+
*with quotes*, so a raw `SELECT actor` is a string that resolves against no one — and
|
|
372
|
+
the cursor must be the event `id`, never `occurred_at`, which is identical across every
|
|
373
|
+
event a single operation emits. Your permission check stays your line, above the call.
|
|
374
|
+
`readHistory` is the same walk plus the payload, the permissions that authorized the
|
|
375
|
+
write — each with the grant it resolved through, `null` for a row written before that
|
|
376
|
+
was recorded, which is not the same fact as `[]` — and the PII classification,
|
|
377
|
+
`piiClass` with the `subjectId` it is keyed by, so a renderer can decide whether an
|
|
378
|
+
entry is safe to show before it shows it. `subjectId` is null when `piiClass` is
|
|
379
|
+
`none`, and the payload is `null` after that subject's erasure, which is a supported
|
|
380
|
+
answer to render rather than an error.
|
|
366
381
|
|
|
367
382
|
### `src/seed.ts`
|
|
368
383
|
|
package/template/src/routes.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Context, Hono } from 'hono';
|
|
2
|
-
import {
|
|
2
|
+
import { problemResponse } from '@substrat-run/vertical-host';
|
|
3
3
|
import type { ScopeStub } from '@substrat-run/kernel';
|
|
4
4
|
|
|
5
5
|
/**
|
|
@@ -28,21 +28,24 @@ export function mountApi(app: Hono<any, any, any>, resolveStub: ResolveStub): vo
|
|
|
28
28
|
const body = (c: Context) => c.req.json<Record<string, unknown>>();
|
|
29
29
|
|
|
30
30
|
/**
|
|
31
|
-
* One error vocabulary, shared with the platform surface: `
|
|
32
|
-
* (@substrat-run/vertical-host) is the same
|
|
33
|
-
* permission denial is 403, a missing thing 404, a
|
|
34
|
-
* fault 502 — identically on both hosts. "No opinion"
|
|
31
|
+
* One error vocabulary, shared with the platform surface: `problemResponse`
|
|
32
|
+
* (@substrat-run/vertical-host) is built on the same `classifyError` that
|
|
33
|
+
* `mountPlatformSurface` uses, so a permission denial is 403, a missing thing 404, a
|
|
34
|
+
* broken invariant 409, a runtime fault 502 — identically on both hosts. "No opinion"
|
|
35
|
+
* becomes the caller's 400.
|
|
36
|
+
*
|
|
37
|
+
* The body is RFC 9457 `application/problem+json`: a `code` from the closed taxonomy
|
|
38
|
+
* when your throw declared one (`substratError('conflict', …)`), `about:blank` when it
|
|
39
|
+
* did not. `{ error }` rides along for one deprecation window, so a client reading it
|
|
40
|
+
* keeps working while you move to `code`.
|
|
35
41
|
*
|
|
36
42
|
* In `worker.ts` this handler is REPLACED: Hono keeps only the last-registered
|
|
37
43
|
* `onError`, and `mountPlatformSurface` installs its own. That is harmless precisely
|
|
38
|
-
* because both are built on
|
|
39
|
-
* here is what gives `server.ts`, which mounts no platform surface, the same
|
|
44
|
+
* because both are built on the same vocabulary — same input, same answer. Registering
|
|
45
|
+
* it here is what gives `server.ts`, which mounts no platform surface, the same
|
|
46
|
+
* behaviour.
|
|
40
47
|
*/
|
|
41
|
-
app.onError((err, c) =>
|
|
42
|
-
const seen = classifyError(err);
|
|
43
|
-
if (seen) return c.json({ error: seen.message }, seen.status);
|
|
44
|
-
return c.json({ error: err instanceof Error ? err.message : String(err) }, 400);
|
|
45
|
-
});
|
|
48
|
+
app.onError((err, c) => problemResponse(c, err));
|
|
46
49
|
|
|
47
50
|
// -- generic invoke ---------------------------------------------------------
|
|
48
51
|
// The kernel checks a permission inside EVERY operation, so a generic route is
|