@starci/hfs 4.11.16 → 4.11.17
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/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 4.11.17 - 2026-10-30
|
|
4
|
+
|
|
5
|
+
- Fixed: `stemOfEvent` (the event class file name of R151) maps underscores to dashes as well as dots, matching eslint-canon-be's `event-consumer-shape` and the kebab case of `source-suffix`.
|
|
6
|
+
- Fixed: `starci app emit`'s write pass (outDir is the app root) no longer calls `appHasDbTypes`/`writeDbTypes`, whose module (`emit/db-types.mjs`) left with the lite Supabase types in 4.10.0: every write pass aborted after `events.json` and wrote no `schema.graphql` or `openapi.json`. `emitContracts` answers `{ written, skipped, standIns }`; spec: the write pass in `src/input-bounds-emit.spec.mjs`.
|
|
7
|
+
- Changed: the bundled canon pins name ai-router 0.4.0 (mounted keys), hfs 4.11.17, eslint-canon-be 3.8.16, eslint-canon-fe 9.5.18 and the Grammar family 0.15.29. No rule or template changed.
|
|
8
|
+
|
|
3
9
|
## 4.11.16 - 2026-10-30
|
|
4
10
|
|
|
5
11
|
- Fixed: the back-end skeleton's `OriginGuard` (`platform/http-security`, BE-AUTH-5) requires an actual allowed `Origin` header on a cookie-bearing mutation (`POST`, `PUT`, `PATCH`, `DELETE` with a `Cookie` header) and on a door or handler marked with the new `RequireOrigin()` (`http-security.decorators.ts`, metadata `REQUIRE_ORIGIN_KEY`), which covers the start of a cookie-authenticated flow before the first cookie exists. A missing, empty, opaque (`null`) or foreign `Origin` is refused with `HTTP_SECURITY_ORIGIN_REJECTED` (403), and an allowed `Referer` no longer stands in for it there; `ServerToServer` does not exempt such a request. Safe methods, and mutations without a cookie on unmarked doors, keep their rules (Referer fallback when Origin is absent, the `ServerToServer` exemption). Spec `origin.guard.spec.ts`: class and handler markers, the first sign-in, cookie-bearing requests and the preserved admission paths, split into describe groups within the function budget.
|
package/emit/contracts.mjs
CHANGED
|
@@ -27,7 +27,6 @@ import { fileURLToPath } from 'node:url';
|
|
|
27
27
|
import { emitEvents, eventServices, eventsSnapshotPath } from './events.mjs';
|
|
28
28
|
import { openapiPath } from './operations.mjs';
|
|
29
29
|
import { readEnv } from '../runtime/scripts/lib/env.mjs';
|
|
30
|
-
import { createSlotResolver, loadSlotManifest, locateDeclaration, readRepoDeclaration } from '../runtime/scripts/hfs/slots.mjs';
|
|
31
30
|
|
|
32
31
|
/** The stderr prefix of a dependency the worker could not load and stood in for. */
|
|
33
32
|
const STAND_IN = 'stand-in ';
|
|
@@ -75,37 +74,16 @@ function emitApp(repoRoot, app) {
|
|
|
75
74
|
return { graphql: graphql.text, openapi: operations.text, standIns: [...graphql.lines, ...operations.lines] };
|
|
76
75
|
}
|
|
77
76
|
|
|
78
|
-
/**
|
|
79
|
-
* The app-scope slot view of the repository `repoRoot` belongs to: `{ appRoot, resolver }`, or null when no app declaration
|
|
80
|
-
* resolves there (a bare folder is not an app and emits no types). `repoRoot` is the be side folder, so `locateDeclaration`
|
|
81
|
-
* answers the app root above it.
|
|
82
|
-
*/
|
|
83
|
-
function appDbScope(repoRoot) {
|
|
84
|
-
try {
|
|
85
|
-
const { appRoot } = locateDeclaration(repoRoot);
|
|
86
|
-
const manifest = loadSlotManifest();
|
|
87
|
-
return { appRoot, resolver: createSlotResolver(manifest, readRepoDeclaration(manifest, appRoot)) };
|
|
88
|
-
} catch {
|
|
89
|
-
return null;
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
|
|
93
77
|
/**
|
|
94
78
|
* Writes the snapshots of every api app: `contracts/<app>/schema.graphql` when it serves GraphQL and `contracts/<app>/openapi.json`
|
|
95
|
-
* when it has an operation table. Answers `{ written: [paths], skipped: [apps], standIns: { app: [lines] }
|
|
79
|
+
* when it has an operation table. Answers `{ written: [paths], skipped: [apps], standIns: { app: [lines] } }`, `skipped`
|
|
96
80
|
* being the apps with neither. `declaration` is the parsed hfs.json. `outDir` (absolute) redirects the snapshots to another
|
|
97
81
|
* root, keeping their relative paths.
|
|
98
|
-
*
|
|
99
|
-
* On the real write pass (`outDir` is `repoRoot`) the app's database types are written too when the slot `app.db.types`
|
|
100
|
-
* is enabled: `resolver` is the app-scope slot view (built from the declaration at the app root when not passed), `appRoot`
|
|
101
|
-
* overrides where the types land, and `run` is the command runner `writeDbTypes` spawns `psql` with. `types` in the answer
|
|
102
|
-
* is `writeDbTypes`'s `{ path, changed }`, or null for an app without the slot or a redirected pass.
|
|
103
82
|
*/
|
|
104
|
-
export function emitContracts({ repoRoot, declaration, outDir = repoRoot
|
|
83
|
+
export function emitContracts({ repoRoot, declaration, outDir = repoRoot } = {}) {
|
|
105
84
|
const written = [];
|
|
106
85
|
const skipped = [];
|
|
107
86
|
const standIns = {};
|
|
108
|
-
let types = null;
|
|
109
87
|
for (const app of apiApps(declaration)) {
|
|
110
88
|
const emitted = emitApp(repoRoot, app);
|
|
111
89
|
if (emitted.graphql === null && emitted.openapi === null) {
|
|
@@ -130,12 +108,5 @@ export function emitContracts({ repoRoot, declaration, outDir = repoRoot, resolv
|
|
|
130
108
|
fs.writeFileSync(target, events);
|
|
131
109
|
written.push(relative);
|
|
132
110
|
}
|
|
133
|
-
|
|
134
|
-
// mode, and L09 judges drift on the same emitted text through the injected emitTypes (emit/db-types.mjs dbTypesEmitter).
|
|
135
|
-
if (outDir === repoRoot) {
|
|
136
|
-
const scope = resolver === undefined ? appDbScope(repoRoot) : { appRoot: locateDeclaration(repoRoot).appRoot, resolver };
|
|
137
|
-
const target = appRoot ?? scope?.appRoot;
|
|
138
|
-
if (target !== null && target !== undefined && scope?.resolver != null && appHasDbTypes(scope.resolver)) types = writeDbTypes({ root: target, run });
|
|
139
|
-
}
|
|
140
|
-
return { written, skipped, standIns, types };
|
|
111
|
+
return { written, skipped, standIns };
|
|
141
112
|
}
|
package/package.json
CHANGED
|
@@ -31,35 +31,35 @@ pins:
|
|
|
31
31
|
source: packages/event-stream/package.json
|
|
32
32
|
why: 'the one server-sent-events client of every front end (fe-stack event-stream, owner:event-stream-package): the transport client layer (modules/api/sse/clients) creates one instance for the app with the bearer token source and the stream operations follow on it; it carries Last-Event-ID resume, the capped doubling backoff, the heartbeat timeout, the single 401 renewal and the useSWRSubscription adapter, so no app keeps a hand-written reader.'
|
|
33
33
|
'@starci/grammar':
|
|
34
|
-
version: 0.15.
|
|
34
|
+
version: 0.15.29
|
|
35
35
|
group: starci
|
|
36
36
|
install: registry
|
|
37
37
|
side: fe
|
|
38
38
|
source: packages/grammar/package.json
|
|
39
39
|
why: 'the grammar front ends read: the brand layer sets `--font-sans` and `--font-mono` and the grammar reads them; it carries the Input tel kind, the copyable and read-only Input, the IconButton disclosure props and count badge, ChatBubble, FillViewport, Textarea mentions and Enter-to-send, the leading PrimaryRailLayout rail, the plate-less Image, ListRow, the soft Alert, Progress steps, the floating primary IconButton, FlowGraph, the STATE-8 loading views and the bordered selectable TagGroup chips. The runtime source version wins for a @starci package.'
|
|
40
40
|
'@starci/eslint-canon-be':
|
|
41
|
-
version: 3.8.
|
|
41
|
+
version: 3.8.16
|
|
42
42
|
group: starci
|
|
43
43
|
install: registry
|
|
44
44
|
side: be
|
|
45
45
|
source: packages/eslint-canon-be/package.json
|
|
46
46
|
why: 'the back-end ESLint canon: `loadHfs(import.meta.url)` of be/eslint.config.mjs finds the app-root hfs.json (kind app) and gives every linted file the view of the be side, and the project graph is built per side; its bundled runtime copies (slots, canon-pins, failure codes, the architecture machine) put .starcistacks and .sops.yaml at the app root; `starciBeConfig({ hfs: loadHfs(import.meta.url) })` is the typed factory and the BE-CONVENTION laws are its rules.'
|
|
47
47
|
'@starci/eslint-canon-fe':
|
|
48
|
-
version: 9.5.
|
|
48
|
+
version: 9.5.18
|
|
49
49
|
group: starci
|
|
50
50
|
install: registry
|
|
51
51
|
side: fe
|
|
52
52
|
source: packages/eslint-canon-fe/package.json
|
|
53
53
|
why: 'the front-end ESLint canon: `loadHfs(import.meta.url)` of fe/eslint.config.mjs finds the app-root hfs.json (kind app) and gives every linted file the view of the fe side, and the project graph is built per side; its bundled runtime copies (slots, canon-pins, failure codes, the architecture machine) put .starcistacks and .sops.yaml at the app root; the front end has no tests (FE_NO_TESTS R97 in hfs) and no-vietnamese-in-source (R91) applies.'
|
|
54
54
|
'@starci/eslint-grammar-core':
|
|
55
|
-
version: 0.15.
|
|
55
|
+
version: 0.15.29
|
|
56
56
|
group: starci
|
|
57
57
|
install: registry
|
|
58
58
|
side: fe
|
|
59
59
|
source: packages/eslint-grammar-core/package.json
|
|
60
60
|
why: 'the StarCi Grammar component API lint, versioned with @starci/grammar; front ends compose this family package with the universal FE canon.'
|
|
61
61
|
'@starci/eslint-grammar-offset-pop':
|
|
62
|
-
version: 0.15.
|
|
62
|
+
version: 0.15.29
|
|
63
63
|
group: starci
|
|
64
64
|
install: registry
|
|
65
65
|
side: fe
|
|
@@ -99,14 +99,14 @@ pins:
|
|
|
99
99
|
source: packages/test-world/package.json
|
|
100
100
|
why: 'the shared e2e library of every back end (R47, R48), a devDependency of every back end: the warm stack behind toxiproxy, the network-edge fakes, the Nest boot, the typed useTestWorld handle, useSandbox for contract specs and the outage lock. A fake declared https (defineHttpFake) serves TLS with the run''s ephemeral in-memory fake CA and publishes its PEM as w.fake.<name>.ca for the app''s own client to trust; verification is never turned off. Kafka is real own infra (the one digest-pinned apache/kafka KRaft image, a broker listener and proxy per slot, slot-prefixed topics, consumer groups and client ids) and Postgres is schema-per-context (a schema and a login role per context in a shared database). Each worker has its own data slot (<ns>_w<k>) with its own databases, Keycloak realm, leased Redis DB, bucket/collection/topic prefixes, fakes host, toxiproxy proxies and outage lock; a run provisions min(jest workers, workers cap, default 2) slots, and state file protocol 2 pairs exactly with @starci/jest-preset (TEST_WORLD_PAIR_MISMATCH otherwise). A modules world boots real peer apps beside its modules, world.apps.<name>.during(fn) is the outage of a peer app, w.keycloak.clientSecret(client) answers the run-generated secret of a confidential realm client, world.keycloak.events/sessions read the realm user events and live sessions, world.infra.postgresql.connection(name) takes one database connection down while the others serve, and world.resolve and scope.resolve take any Nest token. Every path a declaration names (`stack`, seeds, the realm, Dockerfiles) resolves from the app root (the directory of hfs.json); the globalSetup registers the path aliases as TypeScript resolves them. Its CLI main is exported at ./cli for starci app stack.'
|
|
101
101
|
'@starci/ai-router':
|
|
102
|
-
version: 0.
|
|
102
|
+
version: 0.4.0
|
|
103
103
|
group: starci
|
|
104
104
|
install: registry
|
|
105
105
|
side: be
|
|
106
106
|
source: packages/ai-router/package.json
|
|
107
107
|
why: 'the AI pool router of a back end: a NestJS module that routes OpenAI-compatible chat, stream and embeddings calls across keys, models and providers, with @starci/ai-router/testing for its specs; @starci/jest-preset compiles it as an ESM-only dependency.'
|
|
108
108
|
'@starci/hfs':
|
|
109
|
-
version: 4.11.
|
|
109
|
+
version: 4.11.17
|
|
110
110
|
group: starci
|
|
111
111
|
install: registry
|
|
112
112
|
side: both
|
|
@@ -28,8 +28,9 @@ const TEST_CALLEES = new Set(['it', 'test']);
|
|
|
28
28
|
/** The TypeScript compiler of the app (else of the runtime), or null. */
|
|
29
29
|
const typescriptFor = (repoRoot) => loadTypescript(repoRoot, HERE);
|
|
30
30
|
|
|
31
|
-
/** The event-bus pattern file name of an event name: dots become dashes (`order.placed` -> `order-placed
|
|
32
|
-
|
|
31
|
+
/** The event-bus pattern file name of an event name: dots and underscores become dashes (`order.placed` -> `order-placed`,
|
|
32
|
+
* `installation_changed` -> `installation-changed`), the kebab case source-suffix gives every file. */
|
|
33
|
+
export const stemOfEvent = (name) => name.replaceAll(/[._]/gu, '-');
|
|
33
34
|
|
|
34
35
|
/** The initializer of a `static` property member with an identifier name, parentheses and `as` casts stripped, else null. */
|
|
35
36
|
function staticMemberValue(ts, member) {
|