@jimhoyd/urlcode 0.4.0-alpha.3 → 0.4.2
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/.claude/skills/urlcode-authoring/SKILL.md +10 -0
- package/.claude-plugin/marketplace.json +1 -1
- package/CONTRIBUTING.md +36 -0
- package/README.md +20 -15
- package/ROADMAP.md +25 -16
- package/dist/BUILD-MANIFEST.json +20 -19
- package/dist/authoring.js +15 -1
- package/dist/capability-query.js +0 -1
- package/dist/catalog.js +0 -1
- package/dist/cli.js +24 -7
- package/dist/config.js +1 -1
- package/dist/explain.js +1 -1
- package/dist/extensions.js +78 -1
- package/dist/http-response.js +1 -1
- package/dist/index.js +1 -0
- package/dist/init-with.js +36 -11
- package/dist/manifest.js +1 -1
- package/dist/mcp-authoring.js +2 -2
- package/dist/mcp.js +1 -1
- package/dist/policies/cache.js +2 -2
- package/dist/policy.js +16 -0
- package/dist/project-dependencies.js +305 -0
- package/dist/readiness.js +5 -1
- package/dist/runtime.js +1 -1
- package/dist/tooling.js +2 -1
- package/dist/trusted-functions.js +4 -5
- package/dist/types/authoring.d.ts +9 -1
- package/dist/types/capability-query.d.ts +0 -1
- package/dist/types/catalog.d.ts +0 -4
- package/dist/types/config.d.ts +1 -9
- package/dist/types/explain.d.ts +0 -1
- package/dist/types/extensions.d.ts +58 -0
- package/dist/types/http-response.d.ts +0 -1
- package/dist/types/index.d.ts +1 -0
- package/dist/types/init-with.d.ts +7 -13
- package/dist/types/manifest.d.ts +0 -1
- package/dist/types/project-dependencies.d.ts +78 -0
- package/dist/types/readiness.d.ts +3 -0
- package/dist/types/tooling.d.ts +1 -0
- package/dist/types/trusted-functions.d.ts +1 -4
- package/docs/AI-AUTHORING.md +10 -5
- package/docs/AWS.md +9 -0
- package/docs/CI-FOLLOWUP-2026-09-19.md +1 -1
- package/docs/CODEBASE-AUDIT-2026-09-20.md +6 -0
- package/docs/COMPOSING-A-SITE.md +287 -0
- package/docs/CONTAINER-PROMOTION.md +74 -0
- package/docs/DEVELOPMENT-PIPELINE.md +242 -119
- package/docs/EXTENSIONS.md +88 -93
- package/docs/FRAMEWORK.md +45 -30
- package/docs/FUNCTION-SECURITY.md +5 -8
- package/docs/INSTALL.md +13 -8
- package/docs/MIDDLEWARE.md +10 -4
- package/docs/OPEN-DECISIONS.md +64 -99
- package/docs/READINESS.md +8 -4
- package/docs/README.md +18 -13
- package/docs/RELEASE-0.4.1.md +73 -0
- package/docs/RELEASE-0.4.2.md +30 -0
- package/docs/RELEASE-READINESS.md +40 -11
- package/docs/RELEASE-SECURITY.md +33 -14
- package/docs/SPECIFICATION.md +5 -1
- package/docs/SPIKE-CORE-LAYERING.md +1 -1
- package/docs/SPIKE-DEFAULT-TRUST-MODEL.md +9 -13
- package/docs/STARTERS.md +17 -5
- package/docs/TOOLING.md +7 -5
- package/docs/VERCEL.md +10 -2
- package/docs/VERSION-ALIGNMENT.md +50 -8
- package/docs/archive/2026-09-19/ROADMAP.md +1 -0
- package/docs/archive/2026-09-19/SPIKE-EXTENSION-MODEL.md +1 -0
- package/docs/{SPIKE-LAMBDA-COMPILE.md → archive/2026-09-19/SPIKE-LAMBDA-COMPILE.md} +168 -12
- package/docs/archive/2026-09-19/SPIKE-MONOREPO.md +2 -0
- package/docs/archive/2026-09-20/OPEN-DECISIONS-COMPLETED.md +116 -0
- package/docs/archive/README.md +2 -0
- package/docs/yaml/functions.md +10 -2
- package/docs/yaml/middleware.md +5 -3
- package/examples/cookbook/middleware/envelope.mjs +4 -2
- package/llms-full.txt +458 -143
- package/llms.txt +2 -1
- package/package.json +8 -5
- package/packaging/claude-plugin/.claude-plugin/plugin.json +1 -1
- package/packaging/claude-plugin/skills/urlcode-authoring/SKILL.md +10 -0
- package/recipes/middleware/middleware/envelope.mjs +4 -2
- package/skills/urlcode/SKILL.md +8 -1
|
@@ -174,19 +174,15 @@ with `add_repo` before treating it as settled.
|
|
|
174
174
|
settles this explicitly: a hook a project names in an extension's own YAML
|
|
175
175
|
config (`urlcode-auth`'s `onSignUp`/`beforeRegister`/`onDelete` and the
|
|
176
176
|
like) is first-party project code in the same category as any
|
|
177
|
-
`function`/`middleware` route
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
[FUNCTION-SECURITY.md](FUNCTION-SECURITY.md)
|
|
186
|
-
closes that: the same worker/QuickJS engine `FunctionPool` already used for
|
|
187
|
-
route dispatch, generalized to explicit `{source, export}` entries/targets
|
|
188
|
-
instead of `FunctionRoute`, with no second engine and no "trusted" mode
|
|
189
|
-
exported alongside it.
|
|
177
|
+
`function`/`middleware` route and runs trusted and in-process. Core now owns
|
|
178
|
+
the shared extension-hook primitive: it validates hook references, imports
|
|
179
|
+
them during activation, enforces the extension-published input/output JSON
|
|
180
|
+
Schemas, reports those contracts to authoring tools, and includes hook entry
|
|
181
|
+
files in the project revision. Hook sandboxing is not part of this first
|
|
182
|
+
contract; `sandbox: true` is rejected during activation instead of silently
|
|
183
|
+
running trusted. A later sandbox contract must define its serialization and
|
|
184
|
+
capability boundary before it can reuse the worker/QuickJS path described in
|
|
185
|
+
[FUNCTION-SECURITY.md](FUNCTION-SECURITY.md).
|
|
190
186
|
|
|
191
187
|
## Recommended sequencing
|
|
192
188
|
|
package/docs/STARTERS.md
CHANGED
|
@@ -36,18 +36,30 @@ on every push and pull request once the project is on GitHub.
|
|
|
36
36
|
See [organization](ORGANIZATION.md) for choosing your own layout. Defaults allow
|
|
37
37
|
GET/HEAD and use redirect 302; add configuration only when changing behavior.
|
|
38
38
|
|
|
39
|
+
`urlcode init` writes no `package.json`: the route project is route-only, and its
|
|
40
|
+
runtime may be installed globally, in a parent workspace or in a container. Add
|
|
41
|
+
`--manifest` to also write one pinning the runtime at exactly the version that
|
|
42
|
+
generated the project, then run `npm install` in it yourself to install that
|
|
43
|
+
version and produce a lockfile. The CLI never runs a package manager, and no
|
|
44
|
+
upgrade command exists — a pinned version changes when you edit the manifest.
|
|
45
|
+
|
|
39
46
|
Initialization refuses an existing destination. Own the app in your own repository,
|
|
40
47
|
keep secrets out of Git, and upgrade the runtime separately without regenerating
|
|
41
48
|
application files. Add pages, downloads, more functions and business-specific
|
|
42
49
|
features to this same project. Update tests and the expected route count as it grows.
|
|
43
50
|
See [readiness](READINESS.md) and [security](FUNCTION-SECURITY.md).
|
|
44
51
|
|
|
45
|
-
To start an extended site instead, install the
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
52
|
+
To start an extended site instead, install the compatible core, UI, auth and
|
|
53
|
+
admin set from npm in the directory you run from; the
|
|
54
|
+
[framework guide](FRAMEWORK.md#the-composition-contract) provides the exact
|
|
55
|
+
`0.4.1` command to use after publication. Pass UI first so its kit activates
|
|
56
|
+
before auth: `urlcode init ../my-site --with ui,auth,admin` writes the same starter under `my-site/app/`, merges each package's
|
|
49
57
|
routes and declarations into it, and generates one `host.mjs` and README beside
|
|
50
|
-
it.
|
|
58
|
+
it, plus a `package.json` pinning the runtime, those packages and their declared
|
|
59
|
+
peers at the versions it just resolved, validated together against every declared
|
|
60
|
+
peer range. Installing them is your explicit `npm install` in that directory.
|
|
61
|
+
The contract each package fulfils is in [extensions](EXTENSIONS.md#scaffolding-with-init---with),
|
|
62
|
+
with `--no-manifest` and `--pin` in [recorded versions](EXTENSIONS.md#recorded-versions).
|
|
51
63
|
|
|
52
64
|
Both paths carry an `AGENTS.md` for repository-aware assistants. `urlcode init`
|
|
53
65
|
generates it from the installed runtime's capability catalog (the same source as
|
package/docs/TOOLING.md
CHANGED
|
@@ -52,7 +52,7 @@ The tooling API consolidates authoring operations without starting a runtime:
|
|
|
52
52
|
- `listRecipes()` and `showRecipe(name)` expose the fixed bundled recipe catalog.
|
|
53
53
|
- `inspectExtensions({project, hostFile?})` reports each operator-registered
|
|
54
54
|
extension's name, contract version, targets, credential headers, configuration
|
|
55
|
-
and policy JSON Schemas, whether the project declares it, whether its revision
|
|
55
|
+
and policy JSON Schemas, machine-readable project hook contracts, whether the project declares it, whether its revision
|
|
56
56
|
pin matches and where routes mount or require it, plus the project's declared
|
|
57
57
|
names. With `hostFile` it executes that trusted operator module under the
|
|
58
58
|
`--host-file` rules (absolute path, outside the project) and releases it
|
|
@@ -81,10 +81,12 @@ network. Keys always appear in this order:
|
|
|
81
81
|
`--host-file`, `host` counts the operator module's extensions and plugins
|
|
82
82
|
without activating them.
|
|
83
83
|
- `routes`: path, methods and handler per route, sorted by path.
|
|
84
|
-
- `constraints`: a fixed list that holds for every project (
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
84
|
+
- `constraints`: a fixed list that holds for every project (network and Node
|
|
85
|
+
built-ins available to trusted code and withdrawn by `sandbox: true`, no
|
|
86
|
+
regex routes, one handler per route, exact or `{param}` path segments,
|
|
87
|
+
subtree mounts only for static and extension routes, no YAML interpolation,
|
|
88
|
+
injected `env`/`secrets` by operator grant only), each with a value and a
|
|
89
|
+
note spelling out how it differs between the two trust modes.
|
|
88
90
|
- `targets`: for each capability target (or the one `--target`), which of this
|
|
89
91
|
project's used features are supported, conditional, refused or unknown.
|
|
90
92
|
- `commands`: the exact `validate`, `test`, `audit --expect-routes N` (N is
|
package/docs/VERCEL.md
CHANGED
|
@@ -7,7 +7,8 @@ that is the point of the project format.
|
|
|
7
7
|
**This adapter serves native handlers only:** redirects, validated responses,
|
|
8
8
|
pages, static assets and downloads. `function` and `middleware` routes are
|
|
9
9
|
refused at activation, trusted or sandboxed alike, not per request, so a deployment cannot
|
|
10
|
-
half-work
|
|
10
|
+
half-work, and that is a settled position rather than a pending limitation. See
|
|
11
|
+
[what is not supported](#what-this-adapter-does-not-do).
|
|
11
12
|
|
|
12
13
|
A working project is in [`examples/vercel/`](../examples/vercel/).
|
|
13
14
|
|
|
@@ -71,13 +72,20 @@ and want it in generated URLs.
|
|
|
71
72
|
|
|
72
73
|
| Not supported | Why |
|
|
73
74
|
|---|---|
|
|
74
|
-
| `function` routes | They need the self-hosted Node lifecycle; a `sandbox: true` route would additionally spawn worker threads and load the WASM engine on every cold start. Correctness is not the issue;
|
|
75
|
+
| `function` routes | They need the self-hosted Node lifecycle; a `sandbox: true` route would additionally spawn worker threads and load the WASM engine on every cold start. Correctness is not the issue; the execution model is — per-route compilation was considered and declined. |
|
|
75
76
|
| Middleware | Runs in the same execution mode as the route's function, and is refused with it. |
|
|
76
77
|
| `urlcode serve` operational endpoints | `/_urlcode/health` and `/_urlcode/ready` describe a long-lived process. Use Vercel's own observability. |
|
|
77
78
|
|
|
78
79
|
Each refusal happens at activation with a message naming the route, so you find
|
|
79
80
|
out on deploy rather than on a request.
|
|
80
81
|
|
|
82
|
+
`function` and `middleware` are not coming to this adapter. The supported answer
|
|
83
|
+
is to deploy the project as one trusted Node process — a container or a VM
|
|
84
|
+
running the project as it runs locally — which supports every route type today,
|
|
85
|
+
on any host you like including AWS (ECS, EC2, App Runner). See
|
|
86
|
+
[the decision](OPEN-DECISIONS.md#accepted-one-node-deployment-per-project) and
|
|
87
|
+
[the analysis behind it](archive/2026-09-19/SPIKE-LAMBDA-COMPILE.md).
|
|
88
|
+
|
|
81
89
|
## Operating it
|
|
82
90
|
|
|
83
91
|
Every instance activates the project independently: parsing YAML, snapshotting
|
|
@@ -26,18 +26,60 @@ must match its manifest. `npm run release:check` rejects stale lockfile versions
|
|
|
26
26
|
Unreleased source changes do not require moving a published tag or pretending a
|
|
27
27
|
new package has already shipped.
|
|
28
28
|
|
|
29
|
+
Manual GitHub Actions releases can select `core`, `ui`, `auth`, `admin`, or
|
|
30
|
+
`all`. A single-package release updates only that package's manifest, lock entry,
|
|
31
|
+
changelog and relevant Changesets; core also owns its duplicated CLI/MCP/plugin
|
|
32
|
+
version metadata and downstream starter update. The all-packages action aligns
|
|
33
|
+
every manifest and advances internal peer floors together. Changesets that name
|
|
34
|
+
packages across the selected boundary must be released together rather than
|
|
35
|
+
partially consumed.
|
|
36
|
+
|
|
37
|
+
The `0.4.1` release is an explicit stable release decision for core, UI, auth
|
|
38
|
+
and admin. Publication moves each package's npm `latest` channel to `0.4.1`, in
|
|
39
|
+
core → UI → auth → admin order, after its release checks pass. A prepared
|
|
40
|
+
manifest or merged release PR does not prove registry publication: use
|
|
41
|
+
`npm run release:status` to inspect the live result before installing the set.
|
|
42
|
+
This alignment does not permanently couple package versions; subsequent
|
|
43
|
+
releases can still select only the packages that changed.
|
|
44
|
+
|
|
29
45
|
Alpha releases publish under `alpha`; they never automatically move npm
|
|
30
|
-
`latest`.
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
`
|
|
36
|
-
|
|
46
|
+
`latest`. Stable publication does not move `alpha`, so the two channels can
|
|
47
|
+
legitimately show different versions. Test the install combination you recommend
|
|
48
|
+
against peer ranges. `release:status` reports each declared peer floor and
|
|
49
|
+
whether its current `latest` and `alpha` satisfy the range.
|
|
50
|
+
|
|
51
|
+
After all four `0.4.1` versions are published, install the aligned set with:
|
|
52
|
+
|
|
53
|
+
```sh
|
|
54
|
+
npm install --save-exact @jimhoyd/urlcode@0.4.1 @jimhoyd/urlcode-ui@0.4.1 @jimhoyd/urlcode-auth@0.4.1 @jimhoyd/urlcode-admin@0.4.1
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Bare package names resolve npm's current `latest`; exact application pins and a
|
|
58
|
+
committed lockfile keep an existing application from changing on a new release.
|
|
59
|
+
|
|
60
|
+
## Generated applications
|
|
61
|
+
|
|
62
|
+
A generated application records its own versions. `urlcode init --with` writes a
|
|
63
|
+
`package.json` pinning the running runtime, the named extensions and their
|
|
64
|
+
declared peers at the exact versions resolved at generation time, after checking
|
|
65
|
+
that set against every declared peer range; `urlcode init --manifest` does the
|
|
66
|
+
same for a route-only project with the runtime alone; `urlcode-auth init` pins
|
|
67
|
+
this package and its peers. Plain `urlcode init` stays route-only and writes no
|
|
68
|
+
manifest, for projects whose runtime is managed elsewhere. Generation never runs
|
|
69
|
+
a package manager: `package-lock.json` exists only after the operator runs
|
|
70
|
+
`npm install` in the generated directory, and a pin taken from a local path or
|
|
71
|
+
tarball reproduces only where that path exists.
|
|
72
|
+
|
|
73
|
+
No upgrade command exists. A generated project moves to new versions by an
|
|
74
|
+
operator editing its manifest and re-installing. The issue that asked for this
|
|
75
|
+
(#212) describes a future command that would choose a tested compatible set,
|
|
76
|
+
show the changes and require explicit alpha selection; nothing here implements
|
|
77
|
+
that, and the pins above are only the groundwork it would need.
|
|
37
78
|
|
|
38
79
|
The standalone `urlcode-template` is an external exact-version consumer: after a
|
|
39
80
|
runtime release, update its dependency and starter through its own reviewed PR.
|
|
40
|
-
|
|
81
|
+
The coordinator prepares and checks that PR after registry installation succeeds;
|
|
82
|
+
the template is a consumer update, not a fifth npm package. The retired
|
|
41
83
|
`urlcode-docs`, `urlcode-middleware`, `urlcode-dynamic-link` and `urlcode-short`
|
|
42
84
|
repositories are not release targets.
|
|
43
85
|
|
|
@@ -6,6 +6,7 @@ current instructions. See the [current roadmap](../../../ROADMAP.md),
|
|
|
6
6
|
Remaining acceptance work is not declared complete by archiving this record.
|
|
7
7
|
|
|
8
8
|
<!-- trust-model-prose: historical-file -->
|
|
9
|
+
<!-- local-links: historical-file -->
|
|
9
10
|
<!-- guidance-claims: ignore-file -->
|
|
10
11
|
|
|
11
12
|
# Public roadmap
|
|
@@ -6,6 +6,7 @@ current instructions. See the [current roadmap](../../../ROADMAP.md),
|
|
|
6
6
|
Remaining acceptance work is not declared complete by archiving this record.
|
|
7
7
|
|
|
8
8
|
<!-- trust-model-prose: historical-file -->
|
|
9
|
+
<!-- local-links: historical-file -->
|
|
9
10
|
<!-- guidance-claims: ignore-file -->
|
|
10
11
|
|
|
11
12
|
# Review: the extension model, its precedents and its alignment
|
|
@@ -1,19 +1,39 @@
|
|
|
1
|
+
# Historical record
|
|
2
|
+
|
|
3
|
+
Archived 2026-09-19, when the proposal was **declined**. This records an earlier
|
|
4
|
+
proposal, not current instructions. The decision it lost to is
|
|
5
|
+
[one Node deployment per project](../../OPEN-DECISIONS.md#accepted-one-node-deployment-per-project);
|
|
6
|
+
see also the [current roadmap](../../../ROADMAP.md) and
|
|
7
|
+
[current contract](../../SPECIFICATION.md). Remaining acceptance work is not
|
|
8
|
+
declared complete by archiving this record.
|
|
9
|
+
|
|
10
|
+
<!-- trust-model-prose: historical-file -->
|
|
11
|
+
<!-- guidance-claims: ignore-file -->
|
|
12
|
+
|
|
1
13
|
# Spike: compiling `function` routes into their own Lambdas
|
|
2
14
|
|
|
3
15
|
> Review update, 2026-09-19: Current baseline: trusted Node execution is the default, so fresh invocation
|
|
4
16
|
> state is a guarantee of `sandbox: true` only. AWS/Vercel still reject functions
|
|
5
17
|
> and middleware in `src/capabilities.ts`; changing the default did not implement
|
|
6
|
-
> a provider adapter for them.
|
|
7
|
-
>
|
|
8
|
-
> link APIs in the analysis below are historical.
|
|
18
|
+
> a provider adapter for them. The per-route lowering was weighed against a
|
|
19
|
+
> single trusted Node deployment per project, and **the Node deployment was
|
|
20
|
+
> chosen** (2026-09-19). Removed link APIs in the analysis below are historical.
|
|
9
21
|
|
|
10
22
|
|
|
11
|
-
Status:
|
|
12
|
-
|
|
23
|
+
Status: **declined 2026-09-19 — kept as the analysis behind that decision, not
|
|
24
|
+
as a plan.** Nothing here is implemented and nothing here is committed scope.
|
|
25
|
+
The maintainer chose the alternative this document weighs itself against: a
|
|
26
|
+
project that uses `function` or `middleware` deploys as **one trusted Node
|
|
27
|
+
process**, and AWS/Vercel keep refusing those routes at activation as a
|
|
28
|
+
deliberate position rather than a gap awaiting an adapter. See
|
|
29
|
+
[open decisions](../../OPEN-DECISIONS.md#accepted-one-node-deployment-per-project).
|
|
30
|
+
|
|
31
|
+
Read on for why, and for what a first attempt would look like if real demand for
|
|
32
|
+
`function` routes on AWS serverless ever appears and reopens the question.
|
|
13
33
|
|
|
14
34
|
AWS already deploys today. `createLambdaHandler` (`src/aws.ts`) runs a project
|
|
15
35
|
as **one** Lambda behind a Function URL or an API Gateway HTTP API, reading the
|
|
16
|
-
same `urlcode.yaml` that runs locally — see [AWS](AWS.md). What it cannot serve
|
|
36
|
+
same `urlcode.yaml` that runs locally — see [AWS](../../AWS.md). What it cannot serve
|
|
17
37
|
is `function`, `middleware` and `link`, which
|
|
18
38
|
`activateNativeOnly` (`src/adapters.ts`) refuses for the whole deployment at
|
|
19
39
|
activation rather than letting individual routes fail per request.
|
|
@@ -22,6 +42,120 @@ This spike asks one question: **is the refusal of `function` a fact about
|
|
|
22
42
|
Lambda, or a fact about the adapter?** It argues the second, sketches the
|
|
23
43
|
lowering that follows, and is deliberate about what that lowering costs.
|
|
24
44
|
|
|
45
|
+
Section 0 says all of that again without the jargon; sections 1 onward are the
|
|
46
|
+
argument in full.
|
|
47
|
+
|
|
48
|
+
## 0. In plain terms
|
|
49
|
+
|
|
50
|
+
This section is the whole spike in ordinary language. Everything after it is the
|
|
51
|
+
same argument stated precisely; if the two ever disagree, the precise version
|
|
52
|
+
wins.
|
|
53
|
+
|
|
54
|
+
### What a `function` route is
|
|
55
|
+
|
|
56
|
+
Most of a URLCode project is description, not code: "this URL redirects there",
|
|
57
|
+
"this URL returns that page", "this URL serves that file". You write those in
|
|
58
|
+
YAML and never write a program. A **`function` route** is the escape hatch for
|
|
59
|
+
the cases YAML cannot describe — you write a small piece of JavaScript, and the
|
|
60
|
+
runtime calls it when a request arrives. `middleware` is the same idea, except
|
|
61
|
+
it runs on the way *to* other routes rather than answering by itself.
|
|
62
|
+
|
|
63
|
+
### Where that code runs today
|
|
64
|
+
|
|
65
|
+
Two ways, and the project picks per route:
|
|
66
|
+
|
|
67
|
+
- **Trusted (the default).** The code runs directly in the same Node process as
|
|
68
|
+
everything else, at full speed, with the same access the process has. This is
|
|
69
|
+
the default because the person writing the route is usually the same person
|
|
70
|
+
deploying it. See [the decision](../../SPIKE-DEFAULT-TRUST-MODEL.md).
|
|
71
|
+
- **Sandboxed (`sandbox: true`, opt in per route).** The code runs inside a
|
|
72
|
+
small, separate JavaScript engine (QuickJS, compiled to WebAssembly) on a
|
|
73
|
+
worker thread. Inside there it has no filesystem, no network, no environment
|
|
74
|
+
variables and a completely fresh memory space on every single call. It can
|
|
75
|
+
only reach the outside world through *bindings* an operator granted by name.
|
|
76
|
+
See [function security](../../FUNCTION-SECURITY.md).
|
|
77
|
+
|
|
78
|
+
Think of the sandbox as a sealed room inside your own house. Nothing gets in or
|
|
79
|
+
out except through a hatch you deliberately opened, and the room is wiped clean
|
|
80
|
+
between visitors.
|
|
81
|
+
|
|
82
|
+
### What happens on AWS today
|
|
83
|
+
|
|
84
|
+
Nothing, for these routes. If a project contains any `function` or `middleware`
|
|
85
|
+
route, the AWS adapter refuses **the entire deployment at startup** and names
|
|
86
|
+
what it cannot serve. Everything else — redirects, pages, static files,
|
|
87
|
+
downloads — deploys fine as a single Lambda.
|
|
88
|
+
|
|
89
|
+
That refusal is not a bug or an oversight. It is the runtime declining to
|
|
90
|
+
pretend. The alternative would be accepting the deployment and then failing
|
|
91
|
+
individual requests in production, which is worse.
|
|
92
|
+
|
|
93
|
+
### Why it refuses
|
|
94
|
+
|
|
95
|
+
The sealed room is expensive to build. Every time AWS starts a fresh copy of
|
|
96
|
+
your Lambda (a "cold start"), that copy would have to spin up worker threads and
|
|
97
|
+
boot a WebAssembly engine *before* it could answer the first request. One
|
|
98
|
+
process trying to serve every route in a project, rebuilding all that machinery
|
|
99
|
+
on every cold start, is a bad trade.
|
|
100
|
+
|
|
101
|
+
The key observation of this spike: that reasoning is about **one process serving
|
|
102
|
+
every route**. It is not actually about AWS. Change the unit of deployment and
|
|
103
|
+
the objection evaporates.
|
|
104
|
+
|
|
105
|
+
### The idea
|
|
106
|
+
|
|
107
|
+
Stop shipping one Lambda for the whole project. Instead, at build time, emit
|
|
108
|
+
**one Lambda per `function` route** — plus the existing single Lambda for all
|
|
109
|
+
the ordinary declarative routes.
|
|
110
|
+
|
|
111
|
+
Then you do not need the sealed room at all, because each piece of code already
|
|
112
|
+
has an entire AWS process to itself. The process *is* the wall. This is the same
|
|
113
|
+
move URLCode already makes for Cloudflare: when a platform will not run what the
|
|
114
|
+
adapter needs, compile the project ahead of time instead of adapting at runtime.
|
|
115
|
+
|
|
116
|
+
### The catch — three of them
|
|
117
|
+
|
|
118
|
+
1. **It is a different kind of safety, not more of the same safety.** A Lambda
|
|
119
|
+
is a genuinely separate box, which is good. But out of the box that box has
|
|
120
|
+
working network access, a writable temp disk, ambient environment variables,
|
|
121
|
+
and an AWS identity (an "IAM role") that can reach real infrastructure. The
|
|
122
|
+
sealed room had none of that. So swapping one for the other is a **trade**,
|
|
123
|
+
not an upgrade — and the documentation would have to say exactly which
|
|
124
|
+
guarantee changed rather than announcing "functions work on AWS now". One
|
|
125
|
+
guarantee is simply lost: the sandbox promises fresh memory on every call,
|
|
126
|
+
and AWS reuses warm containers.
|
|
127
|
+
2. **URLCode would start generating security-critical infrastructure.** To keep
|
|
128
|
+
each route's permissions tight, the build would have to write out an IAM role
|
|
129
|
+
per route. A generated permissions file is not an implementation detail you
|
|
130
|
+
can wave away with "don't rely on this" — if it is too generous, it is a
|
|
131
|
+
security hole the project authored.
|
|
132
|
+
3. **It would be an unverified claim wearing a capability's clothes.** Right now
|
|
133
|
+
the project's position on AWS is honest: it reports `deployment: 'unverified'`
|
|
134
|
+
and tells you to deploy the example yourself. Shipping a compiler that emits
|
|
135
|
+
cloud infrastructure nobody on the project has ever actually deployed would
|
|
136
|
+
trade an honest refusal for a bigger unproven promise.
|
|
137
|
+
|
|
138
|
+
### The alternative already sitting on the table — and now chosen
|
|
139
|
+
|
|
140
|
+
Run the project as **one ordinary Node deployment** — a container or a VM. That
|
|
141
|
+
supports `function` and `middleware` today, with no compiler, no generated
|
|
142
|
+
infrastructure and no new security surface. It costs you the serverless
|
|
143
|
+
operating model: something is always running, and you scale it yourself.
|
|
144
|
+
|
|
145
|
+
So the real decision is not "can this be built" — it probably can. It is:
|
|
146
|
+
|
|
147
|
+
> Is there enough demand for running URLCode `function` routes specifically on
|
|
148
|
+
> AWS serverless to justify this project owning an infrastructure compiler and a
|
|
149
|
+
> second, weaker isolation story?
|
|
150
|
+
|
|
151
|
+
**That question was answered on 2026-09-19: no.** Nobody has produced the demand
|
|
152
|
+
evidence, and the plain Node deployment does the job today, so the Node
|
|
153
|
+
deployment is the supported model and the compiler is not being built. The
|
|
154
|
+
refusal on AWS and Vercel stays, and is now a position rather than a gap. See
|
|
155
|
+
[open decisions](../../OPEN-DECISIONS.md#accepted-one-node-deployment-per-project).
|
|
156
|
+
If that demand ever shows up, §6 below already scopes what a first attempt
|
|
157
|
+
would be.
|
|
158
|
+
|
|
25
159
|
## 1. Where the refusal actually comes from
|
|
26
160
|
|
|
27
161
|
`src/capabilities.ts` gives the reason:
|
|
@@ -32,7 +166,7 @@ capability === 'function' ? 'isolated functions need worker threads and the WASM
|
|
|
32
166
|
|
|
33
167
|
That is true of the runtime's *own* mechanism. Isolation for guest code is
|
|
34
168
|
QuickJS inside WebAssembly, driven from worker threads, with the boundaries
|
|
35
|
-
[function security](FUNCTION-SECURITY.md) lists: no `process`, no filesystem,
|
|
169
|
+
[function security](../../FUNCTION-SECURITY.md) lists: no `process`, no filesystem,
|
|
36
170
|
no sockets, no `fetch`, a fresh guest heap per invocation, and bindings denied
|
|
37
171
|
unless an operator granted them by exact name.
|
|
38
172
|
|
|
@@ -49,7 +183,7 @@ Cloudflare already establishes the pattern: where a platform forbids what the
|
|
|
49
183
|
adapter needs, URLCode **compiles ahead of time** instead of adapting at
|
|
50
184
|
runtime. `urlcode build --target cloudflare` (`src/build-cloudflare.ts`) emits
|
|
51
185
|
an artifact the Worker reads, and refuses at build time anything it cannot
|
|
52
|
-
serve, with the route named — see [Cloudflare](CLOUDFLARE.md).
|
|
186
|
+
serve, with the route named — see [Cloudflare](../../CLOUDFLARE.md).
|
|
53
187
|
|
|
54
188
|
The same move for AWS: a build step emits **one Lambda per `function` route**,
|
|
55
189
|
plus the existing native-handler Lambda for everything else.
|
|
@@ -75,6 +209,9 @@ This is not a smaller change than it looks. Three things follow from it.
|
|
|
75
209
|
|
|
76
210
|
This is the claim most likely to be made too early, so it goes first.
|
|
77
211
|
|
|
212
|
+
> **Plainly:** both are real walls, but they are different walls, and neither
|
|
213
|
+
> one is strictly stronger. Read the table as a trade, not a scorecard.
|
|
214
|
+
|
|
78
215
|
QuickJS-WASM and a Lambda are both real isolation. They are **not the same
|
|
79
216
|
isolation**, and neither strictly contains the other:
|
|
80
217
|
|
|
@@ -101,6 +238,12 @@ this project should not make.
|
|
|
101
238
|
|
|
102
239
|
### 3.2 `middleware` is the hard part, not `function`
|
|
103
240
|
|
|
241
|
+
> **Plainly:** a `function` is a dead end — it answers and the request stops
|
|
242
|
+
> there, so giving it its own Lambda is easy. Middleware is a queue of steps
|
|
243
|
+
> every request walks through on its way somewhere else, and a queue split
|
|
244
|
+
> across separate Lambdas has to be either copied into each one or wired up as
|
|
245
|
+
> a chain of calls. Both hurt, so this spike does not attempt middleware.
|
|
246
|
+
|
|
104
247
|
`function` lowers cleanly because it is a leaf. `middleware` is a per-request
|
|
105
248
|
chain, and there are only two ways to lower it, both with a real cost:
|
|
106
249
|
|
|
@@ -124,6 +267,11 @@ section describes was later removed from core; see
|
|
|
124
267
|
|
|
125
268
|
## 4. Emitting infrastructure is a new kind of output
|
|
126
269
|
|
|
270
|
+
> **Plainly:** up to now URLCode has only ever produced things that run *inside*
|
|
271
|
+
> a server it was given. Emitting a CloudFormation stack means it starts
|
|
272
|
+
> producing the cloud account's own configuration — including permissions — and
|
|
273
|
+
> becomes responsible for that being correct as AWS changes underneath it.
|
|
274
|
+
|
|
127
275
|
`examples/aws/template.yaml` is hand-written today. Generating a stack means
|
|
128
276
|
this project starts owning a surface it has never owned:
|
|
129
277
|
|
|
@@ -139,7 +287,7 @@ this project starts owning a surface it has never owned:
|
|
|
139
287
|
|
|
140
288
|
That last point is the one worth holding onto. The project's current position on
|
|
141
289
|
AWS is **honest**: the capability catalog reports `deployment: 'unverified'` for
|
|
142
|
-
every target that is not self-hosted, and [AWS](AWS.md) tells a reader to treat
|
|
290
|
+
every target that is not self-hosted, and [AWS](../../AWS.md) tells a reader to treat
|
|
143
291
|
the limits as unverified until they deploy the example themselves. A compiler
|
|
144
292
|
that emits infrastructure nobody has deployed would be a larger unverified claim
|
|
145
293
|
wearing the clothes of a capability.
|
|
@@ -161,7 +309,9 @@ lowering without anything else in the model changing, and `urlcode capabilities
|
|
|
161
309
|
|
|
162
310
|
## 6. Proposed scope for a first spike
|
|
163
311
|
|
|
164
|
-
Narrow, so that the isolation story stays clean and the win is real
|
|
312
|
+
Narrow, so that the isolation story stays clean and the win is real.
|
|
313
|
+
**Plainly: build the smallest version that proves the idea, and resist every
|
|
314
|
+
tempting extra.**
|
|
165
315
|
|
|
166
316
|
**In:** `function` routes, Function URL only, one Lambda per function route,
|
|
167
317
|
a generated role per route carrying exactly that route's granted bindings,
|
|
@@ -190,13 +340,19 @@ having run it on AWS, and the spike should say so rather than imply otherwise.
|
|
|
190
340
|
|
|
191
341
|
## 8. Open questions
|
|
192
342
|
|
|
343
|
+
These are the things a reviewer should press on. In plain terms: *is losing the
|
|
344
|
+
fresh-state guarantee acceptable at all; should URLCode write the permissions or
|
|
345
|
+
merely describe them; does splitting into many Lambdas quietly weaken the
|
|
346
|
+
policies a project already declares; and is one Lambda per route even the right
|
|
347
|
+
size of piece?*
|
|
348
|
+
|
|
193
349
|
- Is warm-container reuse acceptable at all, given the runtime currently
|
|
194
350
|
*guarantees* fresh per-invocation state? If not, this lowering is wrong for
|
|
195
351
|
any route that relies on that guarantee, and there is no build-time way to
|
|
196
352
|
tell which ones do.
|
|
197
353
|
- Does the generated IAM role belong in URLCode's output, or should the build
|
|
198
354
|
emit a *description* of the permissions each route needs and leave the role to
|
|
199
|
-
the operator — closer to how [function security](FUNCTION-SECURITY.md) already
|
|
355
|
+
the operator — closer to how [function security](../../FUNCTION-SECURITY.md) already
|
|
200
356
|
keeps grants operator-controlled and outside the checkout?
|
|
201
357
|
- Does a per-route Lambda change what `policies` can promise? `throttle` on AWS
|
|
202
358
|
is already `conditional` — "counters are per instance" — and more instances
|
|
@@ -204,6 +360,6 @@ having run it on AWS, and the spike should say so rather than imply otherwise.
|
|
|
204
360
|
- Is one Lambda per route the right granularity, or one per *project* with a
|
|
205
361
|
route parameter, which keeps deployment small but reintroduces a shared
|
|
206
362
|
process?
|
|
207
|
-
- What happens to the 6 MB Lambda response limit ([AWS](AWS.md)) for a function
|
|
363
|
+
- What happens to the 6 MB Lambda response limit ([AWS](../../AWS.md)) for a function
|
|
208
364
|
route that returns a large body — refuse at build time, as the Cloudflare
|
|
209
365
|
target refuses what it cannot serve?
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# Spike: consolidating core, auth, admin and ui into one repo
|
|
2
2
|
|
|
3
|
+
<!-- local-links: historical-file -->
|
|
4
|
+
|
|
3
5
|
> **Closed 2026-09-19: the migration is done, and this document is history.**
|
|
4
6
|
>
|
|
5
7
|
> `urlcode-ui`, `urlcode-auth` and `urlcode-admin` are workspace packages under
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
# Historical record
|
|
2
|
+
|
|
3
|
+
Archived 2026-09-20 from [open decisions](../../OPEN-DECISIONS.md), which was
|
|
4
|
+
reconciled against its issues that day (#242). These sections record completed
|
|
5
|
+
work and dated observations exactly as they stood on 2026-09-19 (core `db375bf`
|
|
6
|
+
and the package sources of that date); they are not current instructions and
|
|
7
|
+
their version numbers, dist-tags, commits and pins are not current. Versions
|
|
8
|
+
and channels: [version alignment](../../VERSION-ALIGNMENT.md) and
|
|
9
|
+
`npm run release:status`. Archiving does not declare any security, deployment,
|
|
10
|
+
recovery or provider check performed. The live middleware decision remains in
|
|
11
|
+
open decisions.
|
|
12
|
+
|
|
13
|
+
<!-- trust-model-prose: historical-file -->
|
|
14
|
+
<!-- guidance-claims: ignore-file -->
|
|
15
|
+
<!-- local-links: historical-file -->
|
|
16
|
+
|
|
17
|
+
## Closed questions removed from the active list
|
|
18
|
+
|
|
19
|
+
- Publishing convention is recorded in [version alignment](../../VERSION-ALIGNMENT.md):
|
|
20
|
+
publishable manifests on main, releases through reviewed tags/workflows.
|
|
21
|
+
- Core `0.4.0-alpha.2` and current extension releases exist; publishing that
|
|
22
|
+
already-shipped version is not a next step.
|
|
23
|
+
- Auth/admin kit adoption and shared form helpers are implemented in their code.
|
|
24
|
+
- The UI primitive fallback is retired, which settles the question this table
|
|
25
|
+
carried. Auth and admin now render every screen through the kit and refuse
|
|
26
|
+
activation without it (`packages/auth/src/auth.ts`: "there is no
|
|
27
|
+
shared-primitive fallback"), so "keep both" no longer describes the code.
|
|
28
|
+
- The template pins `0.4.0-alpha.2`. Its skill differences were read against that
|
|
29
|
+
pin: omitted handlers and advice about the removed management API are stale,
|
|
30
|
+
not intentional older-version behavior.
|
|
31
|
+
- The guidance checks run through `npm run check` inside `verify`; a regex check
|
|
32
|
+
is not a schema validator for every example. Extending its coverage is tracked
|
|
33
|
+
separately, not a reason to weaken review or bypass required checks.
|
|
34
|
+
|
|
35
|
+
## Done: the monorepo migration is complete
|
|
36
|
+
|
|
37
|
+
**Closed 2026-09-19.** `urlcode-ui`, `urlcode-auth` and `urlcode-admin` are
|
|
38
|
+
workspace packages under `packages/`, and all three have been released from
|
|
39
|
+
this repository — `@jimhoyd/urlcode-ui@0.1.0-alpha.6`,
|
|
40
|
+
`@jimhoyd/urlcode-auth@0.1.0-alpha.6`, `@jimhoyd/urlcode-admin@0.1.0-alpha.4`,
|
|
41
|
+
each on `alpha` with `latest` deliberately held behind. Core's dist-tags are
|
|
42
|
+
unchanged. The three source repositories are gone; their history survives only
|
|
43
|
+
as verified `git bundle`s, because the repository allows squash merges only and
|
|
44
|
+
the imported commits did not survive onto `main`.
|
|
45
|
+
|
|
46
|
+
The operational runbook is [DEVELOPMENT-PIPELINE.md](../../DEVELOPMENT-PIPELINE.md)
|
|
47
|
+
and [RELEASE-SECURITY.md](../../RELEASE-SECURITY.md). The plan itself is archived at
|
|
48
|
+
[archive/2026-09-19/SPIKE-MONOREPO.md](../2026-09-19/SPIKE-MONOREPO.md),
|
|
49
|
+
whose closing note records what the plan got wrong — chiefly that its
|
|
50
|
+
strongest argument, the reach of the enforcing checks, only became true after
|
|
51
|
+
both checkers were changed to discover workspace packages.
|
|
52
|
+
|
|
53
|
+
The section below is kept for the middleware decision it records, which is
|
|
54
|
+
still the reason there is no `packages/middleware`.
|
|
55
|
+
|
|
56
|
+
## Accepted: monorepo first — middleware withdrawn rather than consolidated
|
|
57
|
+
|
|
58
|
+
The maintainer confirmed that monorepo work is starting now. The earlier
|
|
59
|
+
recommendation to keep repositories separate for now is superseded.
|
|
60
|
+
|
|
61
|
+
**Reversed 2026-09-19: the middleware half of this decision no longer applies.**
|
|
62
|
+
This section used to say "move middleware into the monorepo as its own package
|
|
63
|
+
first, preserving its existing API and behavior," and explicitly: "do not
|
|
64
|
+
unpublish or retire the middleware package as part of the initial move."
|
|
65
|
+
That instruction was overtaken. `@jimhoyd/urlcode-middleware` has been
|
|
66
|
+
**unpublished** from npm at `0.1.0-alpha.2` and `jimhoyd-com/urlcode-middleware`
|
|
67
|
+
**deleted** — the package was withdrawn outright instead of migrated, so there
|
|
68
|
+
is no `packages/middleware` to create and no subsequent fold-into-core step.
|
|
69
|
+
The reversal is recorded here rather than deleted because the instruction it
|
|
70
|
+
replaces was explicit, and a reader who remembers it should be able to see that
|
|
71
|
+
it was changed deliberately and not simply forgotten.
|
|
72
|
+
|
|
73
|
+
Nothing was lost in capability terms: per-route middleware is **native to
|
|
74
|
+
core** via the `middleware:` array ([MIDDLEWARE.md](../../MIDDLEWARE.md)), and the
|
|
75
|
+
deleted package only ever offered the same behavior through the extension
|
|
76
|
+
seam. Trust remains the default and explicit sandbox choices keep their
|
|
77
|
+
meaning. The generic extension wrapping hook (`ExtensionInstance.middleware`,
|
|
78
|
+
`RuntimeExtension.cacheSensitive`) stays in core's contract for other
|
|
79
|
+
extensions — it is no longer exercised by any shipped package, which is worth
|
|
80
|
+
knowing before it is assumed to be covered. Static targets continue rejecting
|
|
81
|
+
request-time middleware because there is no server to run it.
|
|
82
|
+
|
|
83
|
+
Migration scope is therefore **core, auth, admin and UI**. Template and the
|
|
84
|
+
distribution tap stay outside that package move. The observed stale peer pins
|
|
85
|
+
and checkout-limited guidance checks strengthen the case for shared
|
|
86
|
+
verification: carry those checks across the new package paths, since merely
|
|
87
|
+
moving files does not prove every generated skill is covered. The cleanup PRs
|
|
88
|
+
that superseded the earlier zero-open-PR survey have since merged, and a fresh
|
|
89
|
+
survey again reports zero open pull requests across all four in-scope
|
|
90
|
+
repositories — re-run it per repository immediately before that repository
|
|
91
|
+
moves rather than trusting this line.
|
|
92
|
+
|
|
93
|
+
The [archived monorepo plan](../2026-09-19/SPIKE-MONOREPO.md) records
|
|
94
|
+
migration context.
|
|
95
|
+
[Issue 172](https://github.com/jimhoyd-com/urlcode/issues/172), which tracked
|
|
96
|
+
"consolidate middleware into core after moving it into the monorepo," was
|
|
97
|
+
**closed on 2026-09-19** as moot — there was nothing left to consolidate.
|
|
98
|
+
Migration starting is not a claim that it has landed.
|
|
99
|
+
|
|
100
|
+
## Source review baseline
|
|
101
|
+
|
|
102
|
+
| Repository | Reviewed commit | Code checked |
|
|
103
|
+
|---|---|---|
|
|
104
|
+
| core | `db375bf` | Runtime dispatch, schema normalization, capabilities, static compiler, prerender, MCP and resource generators |
|
|
105
|
+
| auth | `71957dd` | Lifecycle hooks, UI rendering and shared helper imports |
|
|
106
|
+
| admin | `f3b4882` | UI rendering, auth-service integration and shared helper imports |
|
|
107
|
+
| UI | `0e96f7f` | Shared forms, kit/host exports and copied core contract |
|
|
108
|
+
| ~~middleware~~ | `f201f4b` | Extension wrapping, per-entry sandbox dispatch and scaffolding — **repository deleted 2026-09-19; this baseline is unreachable except through the local `urlcode-middleware.bundle`** |
|
|
109
|
+
| template | `4e09e50` | Exact core pin, generated guide and both vendored skills |
|
|
110
|
+
|
|
111
|
+
The Homebrew tap (`73eaaef`) still selects stable core `0.3.0`; its old trust
|
|
112
|
+
behavior belongs to that pin and must not be rewritten as alpha.2 behavior.
|
|
113
|
+
The other organization tap and Scoop bucket contain Gitroll, not URLCode.
|
|
114
|
+
The deleted documentation repository is historical context, not a second source
|
|
115
|
+
of current contracts; its former GitHub links no longer resolve. This review is targeted source inspection, not an audit of
|
|
116
|
+
every execution path or an independent security assessment.
|
package/docs/archive/README.md
CHANGED
|
@@ -19,6 +19,8 @@ Archived on 2026-09-19 after checking the current code:
|
|
|
19
19
|
| [Extension model review](2026-09-19/SPIKE-EXTENSION-MODEL.md) | Historical design rationale | Current extension contract |
|
|
20
20
|
| [Usability review](2026-09-19/USABILITY-REVIEW.md) | Predates trusted execution and retired packages | New benchmark observations |
|
|
21
21
|
| [Management security](2026-09-19/MANAGEMENT-SECURITY.md) | Describes the removed core link-management API | No current core replacement |
|
|
22
|
+
| [Lambda compile spike](2026-09-19/SPIKE-LAMBDA-COMPILE.md) | Proposal declined 2026-09-19 in favour of one Node deployment per project | Open decisions; AWS and Vercel guides |
|
|
23
|
+
| [Completed decisions (2026-09-20)](2026-09-20/OPEN-DECISIONS-COMPLETED.md) | Closed questions, the completed monorepo migration and the 2026-09-19 source baseline, moved out of open decisions (#242) | Current open decisions and release readiness |
|
|
22
24
|
|
|
23
25
|
Open proposals stay outside the archive with their status stated at the top.
|
|
24
26
|
The accepted trust-by-default decision remains directly linked from the current
|
package/docs/yaml/functions.md
CHANGED
|
@@ -53,8 +53,16 @@ short form: `middleware: [middleware/headers.mjs]` means
|
|
|
53
53
|
`middleware: [{source: middleware/headers.mjs}]`. The path must be
|
|
54
54
|
project-relative, end in `.mjs` or `.js` and contain no `..` segment.
|
|
55
55
|
Methods default to GET and HEAD. Function paths resolve from the project root,
|
|
56
|
-
not the YAML file's directory.
|
|
57
|
-
|
|
56
|
+
not the YAML file's directory. Modules are ES modules either way: `.mjs` always
|
|
57
|
+
works, while a trusted `.js` module is loaded by Node and therefore needs the
|
|
58
|
+
nearest `package.json` to say `"type": "module"` (a `sandbox: true` route reads
|
|
59
|
+
`.js` as ESM regardless). Serving never transpiles TypeScript; build it first
|
|
60
|
+
with [TypeScript authoring](../TYPESCRIPT-AUTHORING.md). The route above
|
|
61
|
+
declares no `sandbox`, so it runs trusted and in-process: Node APIs, npm
|
|
62
|
+
imports, network and filesystem access are all available to it, exactly as they
|
|
63
|
+
are to any other Node code in the host. Add `sandbox: true` to trade them away
|
|
64
|
+
for isolation -- inside the guest none of them exist. See
|
|
65
|
+
[trust model and sandbox opt-in](../FUNCTION-SECURITY.md).
|
|
58
66
|
|
|
59
67
|
## 4. Input types and constraints
|
|
60
68
|
|