@astrale-os/sdk 0.6.0-beta.39 → 0.6.0-beta.40
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/application/workflow/authoring.d.ts +1 -1
- package/dist/application/workflow/durable.d.ts +24 -0
- package/dist/application/workflow/index.d.ts +1 -1
- package/dist/execution/index.d.ts +1 -1
- package/dist/execution/index.js +1 -1
- package/dist/execution/workflows/engine.d.ts +9 -0
- package/dist/execution/workflows/index.d.ts +2 -2
- package/dist/execution/workflows/index.js +1 -1
- package/dist/execution/workflows/options.d.ts +6 -1
- package/dist/execution/workflows/options.js +21 -3
- package/dist/execution/workflows/run.js +95 -9
- package/dist/testing/durable/engine.js +23 -1
- package/dist/tooling/linter/implementations/source/shared.d.ts +5 -0
- package/dist/tooling/linter/implementations/source/shared.js +15 -7
- package/dist/tooling/linter/implementations/source/workflows.js +77 -7
- package/dist/tooling/linter/policy/generated.js +2 -2
- package/dist/tooling/linter/requirements/registry.js +1 -0
- package/docs/admission-age.md +88 -0
- package/docs/auth-api-migration.md +55 -0
- package/docs/business-errors.md +42 -0
- package/docs/domain-definition-migration.md +44 -0
- package/docs/domain-schema-migration.md +36 -0
- package/docs/durable-workflows.md +418 -0
- package/docs/handler-dependencies.md +213 -0
- package/docs/release.md +155 -0
- package/docs/schema-cost.md +124 -0
- package/package.json +3 -2
package/docs/release.md
ADDED
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
# SDK release lifecycle
|
|
2
|
+
|
|
3
|
+
Release Please owns the versions and GitHub releases for the SDK package set:
|
|
4
|
+
|
|
5
|
+
- `@astrale-os/sdk`
|
|
6
|
+
- `@astrale-os/adapter-cloudflare`
|
|
7
|
+
- `@astrale-os/adapter-astrale`
|
|
8
|
+
- `create-astrale-domain`
|
|
9
|
+
|
|
10
|
+
The Node workspace plugin follows local dependency edges. Every internal release edge uses
|
|
11
|
+
`workspace:*`, so a changed SDK refreshes the dependent adapter and scaffolder packages in the same
|
|
12
|
+
Release Please pull request and publication rewrites each edge to that dependency's exact version.
|
|
13
|
+
Never use a caret or tilde for an internal release edge: an immutable adapter must not resolve a
|
|
14
|
+
newer SDK or adapter after publication. The shared publisher consumes those versions; it never
|
|
15
|
+
invents a release version.
|
|
16
|
+
|
|
17
|
+
Dependency installation, authoritative publication, and public qualification are npmjs-only. npm
|
|
18
|
+
publication uses OIDC Trusted Publishing. After that proof completes, the workflow mirrors the exact
|
|
19
|
+
SDK and adapter tarballs to private GitHub Packages copies. GitHub Packages is never a consumer,
|
|
20
|
+
build, installation, or qualification source. Never add a PAT, an npm token, an auth file, or a
|
|
21
|
+
repository package token; no credential may enter a tracked file, lockfile, package archive, or build
|
|
22
|
+
log.
|
|
23
|
+
|
|
24
|
+
## Normal beta release
|
|
25
|
+
|
|
26
|
+
Prerequisite: every exact Kernel dependency in the SDK manifest is already published on npm and the
|
|
27
|
+
SDK passes typecheck, test, build, pack, and fresh-consumer gates against those artifacts. Never use
|
|
28
|
+
a local checkout, tarball, or override to make the release build pass.
|
|
29
|
+
|
|
30
|
+
1. Merge conventional feature and fix pull requests into `main`.
|
|
31
|
+
The squash-merge title is Release Please input: use `feat` for a public capability or API change
|
|
32
|
+
and `fix` for a behavioral defect. Never classify a publication-bearing change as `refactor`,
|
|
33
|
+
`docs`, `chore`, or `test`; those types do not advance the beta
|
|
34
|
+
package cohort.
|
|
35
|
+
2. The `Release` workflow creates or refreshes the single Release Please pull request.
|
|
36
|
+
3. Select **Approve workflows to run** for that bot-authored pull request and wait for every required
|
|
37
|
+
check. GitHub can mark these workflows `action_required`; this is an approval gate, not a passed
|
|
38
|
+
run.
|
|
39
|
+
4. Review every changed package version, exact internal dependency, changelog, and
|
|
40
|
+
`.release-please-manifest.json` entry.
|
|
41
|
+
5. Merge the Release Please pull request.
|
|
42
|
+
6. Release Please creates an immutable GitHub prerelease for every released component. Only then does
|
|
43
|
+
`Release` dispatch the standalone `Publish` workflow with the exact released commit SHA, preserving
|
|
44
|
+
its npm Trusted Publishing identity. Publish rejects a non-SHA or a checkout mismatch before npm I/O.
|
|
45
|
+
7. The shared package publisher builds the workspace and publishes every public package to npm. A
|
|
46
|
+
`-beta.N` version advances npm `beta`; npm `latest` remains on the last stable version.
|
|
47
|
+
8. The same workflow then runs `pnpm run qualify:publications` without a token. This creates isolated
|
|
48
|
+
temporary stores, installs the four exact manifest versions from npmjs, generates a Domain with
|
|
49
|
+
`linkWorkspacePackages: false`, pins that generated project to the exact SDK and adapter versions,
|
|
50
|
+
and runs its typecheck, tests, build, and package command. Any missing publication, transitive
|
|
51
|
+
dependency, wrong version, non-exact internal cohort edge, local source, override, or GitHub
|
|
52
|
+
registry reference fails the workflow.
|
|
53
|
+
9. A separate least-privilege job downloads the exact SDK and adapter tarballs from npm, verifies
|
|
54
|
+
their npm SHA-512 integrity, mirrors them with `GITHUB_TOKEN`, and then requires byte-identical
|
|
55
|
+
GitHub downloads, matching release-channel tags, private visibility, and an exact link to
|
|
56
|
+
`astrale-os/sdk`. The scaffolder remains npm-only.
|
|
57
|
+
|
|
58
|
+
The publisher receives package directories in strict producer order: SDK, Cloudflare adapter,
|
|
59
|
+
Astrale adapter, then scaffolder. The shared action validates this dependency order and completes npm
|
|
60
|
+
plus the exact release-channel dist-tag for one package before it starts the next. A linked workspace
|
|
61
|
+
build is therefore never treated as publication proof.
|
|
62
|
+
|
|
63
|
+
npm publication has no token fallback. All four npm packages must configure GitHub Actions Trusted
|
|
64
|
+
Publishing for this repository and the `publish.yml` workflow. A missing Trusted Publisher or a wrong
|
|
65
|
+
dist-tag is an explicit, fail-closed publication error; do not work around it with a PAT or npm token.
|
|
66
|
+
|
|
67
|
+
The mirror job starts only after the anonymous npm qualification succeeds. It has `packages: write`
|
|
68
|
+
but no npm OIDC permission, performs no build or workspace install, and cannot publish to npm. A
|
|
69
|
+
mirror failure makes `Publish` red without undoing the already-complete npm release. Rerunning the
|
|
70
|
+
failed job is safe: existing versions must be byte-identical, no version is overwritten or deleted,
|
|
71
|
+
and tags are reconciled only when npm still points them at that exact version. Do not backfill older
|
|
72
|
+
version gaps during initial rollout; the next ordinary beta release is the admission proof.
|
|
73
|
+
|
|
74
|
+
Ordinary source pushes do not publish. Do not manually edit package versions, manually dispatch
|
|
75
|
+
`Publish`, or push release tags during the normal flow.
|
|
76
|
+
|
|
77
|
+
## Start the new Auth beta series
|
|
78
|
+
|
|
79
|
+
The session-owned Auth API starts a new SDK beta series at `0.6.0-beta.0`. Its feature PR sets
|
|
80
|
+
only the SDK's package-local `release-as` to that version. Release Please owns the actual manifest
|
|
81
|
+
and cohort bumps. Remove that one-shot override from the generated release PR before merging it.
|
|
82
|
+
This keeps the release on npm `beta`, while existing SDK `0.5.0` prerelease ranges retain the old API.
|
|
83
|
+
|
|
84
|
+
## Promote the package set to stable
|
|
85
|
+
|
|
86
|
+
Create one promotion pull request that removes only these beta settings from
|
|
87
|
+
`.release-please-config.json`:
|
|
88
|
+
|
|
89
|
+
```json
|
|
90
|
+
"versioning": "prerelease",
|
|
91
|
+
"prerelease": true,
|
|
92
|
+
"prerelease-type": "beta"
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Keep the Node workspace plugin, `always-update`, package map, and workflows unchanged. In the same
|
|
96
|
+
promotion pull request, temporarily add these package-local overrides:
|
|
97
|
+
|
|
98
|
+
```json
|
|
99
|
+
"packages": {
|
|
100
|
+
".": { "component": "sdk", "release-as": "0.6.0" },
|
|
101
|
+
"adapter-cloudflare": { "component": "adapter-cloudflare", "release-as": "0.5.0" },
|
|
102
|
+
"adapter-astrale": { "component": "adapter-astrale", "release-as": "0.5.0" },
|
|
103
|
+
"create-astrale-domain": {
|
|
104
|
+
"component": "create-astrale-domain",
|
|
105
|
+
"release-as": "0.3.0"
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
After that promotion pull request lands, Release Please proposes those stable versions. Remove all
|
|
111
|
+
four `release-as` properties from the generated stable release pull request, rerun its checks, and
|
|
112
|
+
merge it. This publishes npm `latest` and ordinary GitHub releases. The npm `beta` tags intentionally
|
|
113
|
+
remain on the last beta and do not affect bare installs.
|
|
114
|
+
|
|
115
|
+
## Verification
|
|
116
|
+
|
|
117
|
+
Always name npmjs explicitly when inspecting releases so the verification also proves the public
|
|
118
|
+
registry boundary.
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
gh run list --repo astrale-os/sdk --workflow Release --limit 5
|
|
122
|
+
gh run list --repo astrale-os/sdk --workflow Publish --limit 5
|
|
123
|
+
npm view @astrale-os/sdk dist-tags --json --registry=https://registry.npmjs.org
|
|
124
|
+
npm view @astrale-os/adapter-cloudflare dist-tags --json --registry=https://registry.npmjs.org
|
|
125
|
+
npm view @astrale-os/adapter-astrale dist-tags --json --registry=https://registry.npmjs.org
|
|
126
|
+
npm view create-astrale-domain dist-tags --json --registry=https://registry.npmjs.org
|
|
127
|
+
gh api orgs/astrale-os/packages/npm/sdk --jq '{visibility,repository:.repository.full_name}'
|
|
128
|
+
gh api orgs/astrale-os/packages/npm/adapter-cloudflare --jq '{visibility,repository:.repository.full_name}'
|
|
129
|
+
gh api orgs/astrale-os/packages/npm/adapter-astrale --jq '{visibility,repository:.repository.full_name}'
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
The SDK, adapters, scaffolder, generated manifest, and every transitive runtime dependency must be
|
|
133
|
+
installable from npmjs without authentication. Prove that boundary from a clean temporary directory:
|
|
134
|
+
|
|
135
|
+
```ini
|
|
136
|
+
# ./npmrc (in the smoke-test parent directory)
|
|
137
|
+
registry=https://registry.npmjs.org/
|
|
138
|
+
always-auth=false
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
Then run the complete smoke test:
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
NPM_CONFIG_USERCONFIG="$PWD/npmrc" npm install @astrale-os/sdk@beta
|
|
145
|
+
NPM_CONFIG_USERCONFIG="$PWD/npmrc" npx -y create-astrale-domain@beta beta-smoke --yes
|
|
146
|
+
cd beta-smoke
|
|
147
|
+
NPM_CONFIG_USERCONFIG="../npmrc" pnpm install --no-frozen-lockfile
|
|
148
|
+
pnpm typecheck
|
|
149
|
+
pnpm test
|
|
150
|
+
pnpm build
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
Inspect the generated lockfile and installed package manifests. They must resolve the released SDK and
|
|
154
|
+
adapter package set and the SDK-owned public Kernel dependencies from npmjs, not `latest`, GitHub
|
|
155
|
+
Packages, workspace links, Git URLs, or an older manually published beta.
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
# What a compiled Schema costs
|
|
2
|
+
|
|
3
|
+
A compiled Domain is large, and its size is not driven by how much the Domain declares. This
|
|
4
|
+
records what the weight is made of, where it is paid, and which levers belong to this repository.
|
|
5
|
+
|
|
6
|
+
Every figure comes from `scripts/bench/`, on synthetic Domains built around `KernelSchema`. Node
|
|
7
|
+
counts and byte weights follow from the compiled format and hold anywhere. **Timings do not.** The
|
|
8
|
+
benches run under Bun, which is JavaScriptCore, and workerd is V8. On the same graph and the same
|
|
9
|
+
walk:
|
|
10
|
+
|
|
11
|
+
| walk | V8 (Node 26) | JavaScriptCore (Bun) |
|
|
12
|
+
| --- | --- | --- |
|
|
13
|
+
| seal, as shipped | 10.4 ms | 73.7 ms |
|
|
14
|
+
| seal, arrays indexed and primitives skipped | 5.9 ms | 48.2 ms |
|
|
15
|
+
|
|
16
|
+
Close to an order of magnitude apart. Read every millisecond below as a JavaScriptCore
|
|
17
|
+
millisecond: useful for ranking two strategies against each other, worthless as a production
|
|
18
|
+
figure. Any claim about cold start has to be re-measured on the real bundle in workerd.
|
|
19
|
+
|
|
20
|
+
## The cost law
|
|
21
|
+
|
|
22
|
+
Growing a Domain two ways, plain classes against classes carrying one format-constrained
|
|
23
|
+
property (`bun scripts/bench/schema-cost.ts`):
|
|
24
|
+
|
|
25
|
+
| classes | plain | one constrained property each |
|
|
26
|
+
| --- | --- | --- |
|
|
27
|
+
| 0 | 19 522 | 19 522 |
|
|
28
|
+
| 5 | 19 587 | 22 157 |
|
|
29
|
+
| 20 | 19 782 | 30 062 |
|
|
30
|
+
| 50 | 20 172 | 45 872 |
|
|
31
|
+
|
|
32
|
+
Three readings, each counter-intuitive.
|
|
33
|
+
|
|
34
|
+
**An empty Domain is not empty.** 19 522 nodes and 554 KiB before anything is declared. That is
|
|
35
|
+
the floor the `KernelSchema` dependency sets, and every Astrale Domain pays it.
|
|
36
|
+
|
|
37
|
+
**Classes and properties are nearly free.** Fifty classes and a hundred plain properties add a
|
|
38
|
+
thousand nodes, about twenty per class. The size of the model is not the problem.
|
|
39
|
+
|
|
40
|
+
**One thing is expensive: a string carrying a format.** The same constraint on fifty classes adds
|
|
41
|
+
26 350 nodes, about **527 nodes per property**, and fifty separate automata for one expression.
|
|
42
|
+
`resolve` and `compile` both roughly double over the same range.
|
|
43
|
+
|
|
44
|
+
## Where the weight is
|
|
45
|
+
|
|
46
|
+
On the Kernel floor, by region (`bun scripts/bench/compiled-graph.ts`):
|
|
47
|
+
|
|
48
|
+
| region | nodes | share |
|
|
49
|
+
| --- | --- | --- |
|
|
50
|
+
| `values[].patterns` | 15 856 | 81.2 % |
|
|
51
|
+
| `values[].nodes` | 3 031 | 15.5 % |
|
|
52
|
+
| `definitions.*` | 563 | 2.9 % |
|
|
53
|
+
| everything else | 6 | 0.0 % |
|
|
54
|
+
|
|
55
|
+
The weight is compiled regular-expression automata. A bounded quantifier is unrolled into one
|
|
56
|
+
state per repetition, so `{64}` becomes 162 states and `{0,253}` becomes 528. Each state carries
|
|
57
|
+
two arrays, each transition an array of ranges, and each range is itself a two-element array.
|
|
58
|
+
That is also why a compiled graph holds more arrays than objects.
|
|
59
|
+
|
|
60
|
+
Nothing is shared between copies. Sixteen automata stand for seven distinct expressions:
|
|
61
|
+
`sha256:[0-9a-f]{64}`, nineteen characters, weighs 576 nodes and exists five times over. Interning
|
|
62
|
+
by source would remove 7 973 of the 15 794 pattern nodes at identical semantics, since two
|
|
63
|
+
automata for one expression are structurally identical and immutable.
|
|
64
|
+
|
|
65
|
+
## Where it is paid
|
|
66
|
+
|
|
67
|
+
1. **At build time**, in `compile`. Once, on a contributor's machine.
|
|
68
|
+
2. **In the Worker bundle.** The material is emitted as an inline literal, so the automata ship as
|
|
69
|
+
bytes: 44 % of a bare compiled Schema, 65 % once a Domain constrains its strings.
|
|
70
|
+
3. **At cold start, the material path.** The emitted literal is allocated, sealed, and realized on
|
|
71
|
+
the first request of every isolate.
|
|
72
|
+
4. **At cold start, the authoring path**, when application code builds the Schema from source at
|
|
73
|
+
runtime. `resolve` is memoised per Schema value, so it is a once-per-isolate cost like the
|
|
74
|
+
material path, not a per-request one.
|
|
75
|
+
5. **In any client** that builds the Schema, a browser included.
|
|
76
|
+
|
|
77
|
+
## Sealing the material
|
|
78
|
+
|
|
79
|
+
`schema.compiled` is almost the whole material graph, so sealing cost is a question of how many
|
|
80
|
+
times that graph is walked (`bun scripts/bench/freeze-strategies.ts`):
|
|
81
|
+
|
|
82
|
+
| strategy | cost |
|
|
83
|
+
| --- | --- |
|
|
84
|
+
| before: two SDK passes and one DSL pass | 131.6 ms |
|
|
85
|
+
| current: one SDK pass and one DSL pass | 74.7 ms |
|
|
86
|
+
| candidate: the DSL owns `schema.compiled` | 62.0 ms |
|
|
87
|
+
|
|
88
|
+
Two results matter more than the ranking.
|
|
89
|
+
|
|
90
|
+
**Most of the remaining cost is not the Schema.** Sealing `schema.compiled` alone is 17.2 ms.
|
|
91
|
+
Sealing `schema.bundle.bytes` alone is 43.9 ms, because it is one array of tens of thousands of
|
|
92
|
+
numbers and the walker calls `Object.values` on it. A walker that indexes arrays in place and
|
|
93
|
+
never recurses into a primitive takes one pass from 61.2 ms to 43.5 ms. `Object.freeze` on an
|
|
94
|
+
array of numbers already seals its elements, so that recursion changes nothing: it is cost
|
|
95
|
+
without a guarantee behind it.
|
|
96
|
+
|
|
97
|
+
**The emitter throws away sharing the compiler produced.** The compiled graph holds 19 522 nodes
|
|
98
|
+
as built and 32 882 after a JSON round trip, because JSON has no references. 13 360 nodes, two
|
|
99
|
+
thirds again, exist only because the material is emitted as a flat literal. Hoisting identical
|
|
100
|
+
subtrees into shared bindings at emission would restore sharing rather than invent it, and would
|
|
101
|
+
cut bundle bytes at the same time.
|
|
102
|
+
|
|
103
|
+
Sealing is a guarantee, not decoration: `realize()` retains live references into the graph, and
|
|
104
|
+
the material is one singleton shared by every concurrent request in the isolate. None of the
|
|
105
|
+
changes above give that up. They stop walking what is already sealed, or what sealing a container
|
|
106
|
+
already covers.
|
|
107
|
+
|
|
108
|
+
## Which levers are here
|
|
109
|
+
|
|
110
|
+
Owned by this repository: how many times the material is walked, how the walker treats a flat
|
|
111
|
+
array, whether the emitter shares identical subtrees, and whether the SDK hands out the Domain
|
|
112
|
+
that a restored Build already carries instead of leaving an application to rebuild it.
|
|
113
|
+
|
|
114
|
+
Owned by `kernel-dsl`: the cost of `resolve`, the pattern table in the compiled format, and the
|
|
115
|
+
unrolling of bounded quantifiers. The last of these pays at all five sites at once and is where
|
|
116
|
+
most of the weight is. None of it can be reached from here.
|
|
117
|
+
|
|
118
|
+
## Verifying a change
|
|
119
|
+
|
|
120
|
+
`scripts/bench/freeze-equivalence.ts` compares the frozen state of every reachable node between
|
|
121
|
+
two sealing implementations and exits non-zero on a divergence or a vacuous walk. Spine assertions
|
|
122
|
+
cannot tell a deep seal from a shallow one: `material()` returns a shallow-frozen envelope, so a
|
|
123
|
+
walker treating a frozen container as proof of a sealed interior would seal nothing and still
|
|
124
|
+
satisfy them. `src/deployment/build/__tests__/realization.test.ts` carries the standing audit.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@astrale-os/sdk",
|
|
3
|
-
"version": "0.6.0-beta.
|
|
3
|
+
"version": "0.6.0-beta.40",
|
|
4
4
|
"description": "Schema-first SDK for defining, composing, and deploying Astrale domains",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"astrale",
|
|
@@ -20,7 +20,8 @@
|
|
|
20
20
|
},
|
|
21
21
|
"files": [
|
|
22
22
|
"dist",
|
|
23
|
-
"!dist/**/*.map"
|
|
23
|
+
"!dist/**/*.map",
|
|
24
|
+
"docs"
|
|
24
25
|
],
|
|
25
26
|
"type": "module",
|
|
26
27
|
"sideEffects": false,
|