Skip to content

feat(stdlib): portable Http primitive — Deno-ESM target (Refs #160)#226

Merged
hyperpolymath merged 1 commit into
mainfrom
stage-c/160-portable-http
May 18, 2026
Merged

feat(stdlib): portable Http primitive — Deno-ESM target (Refs #160)#226
hyperpolymath merged 1 commit into
mainfrom
stage-c/160-portable-http

Conversation

@hyperpolymath
Copy link
Copy Markdown
Owner

What

The C-spine Http stdlib primitive (#160), Deno-ESM target.

stdlib/Http.affine: typed Request/Response, assoc-list headers [(String, String)], Option<String> body, fetch/get/post/request/is_ok. Effect row / { Net, Async } using the reserved built-in Net (cross-module-sound; no per-module effect decl needed).

Design decisions (from the issue thread)

Compiler change (general, not a one-off)

codegen_deno.ml gains a real Async-effect lowering that serves the whole migration spine:

  • functions whose effect row mentions Asyncasync function;
  • async free-fn calls awaited at the call site;
  • __as_httpFetch runtime (assoc-list ↔ header object, Option body, explicit globalThis.fetch to avoid the module-level function fetch shadow);
  • http_request extern → (await __as_httpFetch(...)).

Tests

  • tests/codegen-deno/http_fetch.{affine,harness.mjs} — e2e through the real flattened stdlib/Http.affine, globalThis.fetch stubbed (no network): GET/POST/headers/body/404/is_ok all asserted.
  • Full tools/run_codegen_deno_tests.sh suite green; dune test --force = 258 tests pass (+1 = the auto-picked-up AOT Http smoke case).

Also migrates two pre-existing baseline-rot fixtures (class_basic.affine, ref_fields.affine) from the retired Type { f: x } to Type #{ f: x } (#218 record syntax — the estate sweep skipped in-tree test fixtures). Confirmed present on origin/main; not a #160 change, but required for the Deno suite to be green.

Issue linkage

Refs #160 (deliberately not Closes: #160 stays open until the typed-wasm target also lands — joint-close). Companion: #225 (typed-wasm Http ABI / Ephapax convergence).

🤖 Generated with Claude Code

stdlib/Http.affine: typed `Request`/`Response` with assoc-list headers
`[(String, String)]` and `Option<String>` body; `fetch`/`get`/`post`/
`request`/`is_ok`. Effect row `/ { Net, Async }` (reserved built-in
`Net` — cross-module-sound, no per-module effect decl).

Headers are an assoc list, not Dict: lets #160 land independently of
#162 while preserving spine order #160 -> #161 -> #162; upgrade to Dict
later is source-compatible for callers using the helpers.

codegen_deno.ml — general Async-effect lowering (serves the whole
migration spine, not a one-off):
  * fd_is_async: a fn whose effect row mentions `Async` is emitted as
    `async function` with its body in async context.
  * async free-fn calls are awaited at the call site (so
    `get(u).status` reads the resolved value, not a Promise).
  * __as_httpFetch runtime: assoc-list <-> header object, `Option`
    body, `globalThis.fetch` round-trip (explicit `globalThis.` — the
    stdlib `fetch` compiles to a module-level `function fetch` that
    would otherwise shadow the host).
  * http_request extern lowers to `(await __as_httpFetch(...))`.

tests/codegen-deno/http_fetch.{affine,harness.mjs}: e2e via the real
flattened stdlib/Http.affine, `globalThis.fetch` stubbed (no network)
— GET/POST/headers/body/404/is_ok asserted. Full Deno suite + 258-test
gate green (+1 = auto-picked-up AOT Http smoke case).

Also migrates two pre-existing baseline-rot fixtures
(class_basic.affine, ref_fields.affine) from retired `Type { f: x }`
to `Type #{ f: x }` (#218 record syntax; the estate sweep skipped
in-tree test fixtures). Not a #160 change; required to make the Deno
suite green.

Deno-ESM target only here by design; typed-wasm target (the
convergence ABI shared with Ephapax) is tracked as the next slice in
#225. Refs #160 (stays open until both targets land); companion #225.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@hyperpolymath hyperpolymath merged commit 7018b92 into main May 18, 2026
10 of 12 checks passed
@hyperpolymath hyperpolymath deleted the stage-c/160-portable-http branch May 18, 2026 19:36
hyperpolymath added a commit that referenced this pull request May 18, 2026
…nable foundation

Pivotal finding (recorded in the design doc): the WasmGC backend does
NOT flatten imports — a cross-module `pub fn` is a Wasm import
`<Module>.<fn>`, so stdlib `Http.*` are host imports on wasm (exactly
like the proven `Vscode.httpPostJson`). This splits #225 cleanly:
a host adapter (no compiler-correctness risk, the bulk of the value)
+ the CPS transform (the hard tail, only for user Async fns that
interleave). Does NOT degrade typed-wasm: network I/O is host-mediated
on every wasm target (WASI/JSPI/browser alike); guest logic is still
fully compiled.

PR1 delivers the verified foundation the transform builds on:
- stdlib/Http.affine: `pub extern type Thenable` + `http_request_thenable
  (url, method, body) -> Thenable / { Net, Async }`, mirroring the
  proven httpPostJson/#205 shape. Single portable source surface
  (fetch/get/post) unchanged; this is the wasm-path host-import
  contract, not a second public API.
- tests/codegen/http_thenable_skeleton.affine + harness: wasm e2e
  proving the boundary + #205 protocol end-to-end with a stubbed
  `globalThis.fetch` (no network), pure pass-through (no transform).
- docs/specs/async-on-wasm-cps.adoc: implementation refinement +
  revised 4-PR plan.

Also migrates 5 pre-existing baseline-rot wasm fixtures
(test_record_simple/multiple/multi_field/pattern_record/
tuple_record_array) from retired `{ f: x }` record literals to `#{`
(#218 sweep skipped in-tree wasm fixtures; same as the Deno fixtures
fixed in #226). Record patterns/struct decls correctly left as `{}`.

Gate green: dune test --force 258; full wasm suite green; Http.affine
Deno-AOT clean. Refs #225 #160.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
hyperpolymath added a commit that referenced this pull request May 18, 2026
…on (Refs #160 #225) (#227)

* docs(design): Async-on-wasm transparent CPS transform — proposal (#225)

Design-before-implementation for the typed-wasm Http target (issue
#225, owner-chosen Option 2: one byte-identical fetch->Response surface
on both targets). Selective CPS/continuation transform of Async-effect
functions on the WasmGC backend only, built on the existing #199
closure ABI + #205 thenableThen re-entry. States the correctness
obligations (affine capture / once-resumption / value reconstruction)
and a 4-PR incremental plan, each behind the 258 gate. PROPOSED —
awaiting owner approval; promotes to ADR + typed-wasm convergence ABI
section (Ephapax co-stakeholder) on sign-off.

Refs #225 #160.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* docs(adr): promote Async-on-wasm CPS transform to ADR-013 (#225)

Owner-approved 2026-05-18. SETTLED-DECISIONS.adoc + META.a2ml ADR-013
entry; design doc status PROPOSED -> ACCEPTED. typed-wasm convergence
ABI section + Ephapax co-stakeholder review tracked separately.

Refs #225 #160.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(stdlib,wasm): #225 PR1 — typed-wasm Http skeleton + verified Thenable foundation

Pivotal finding (recorded in the design doc): the WasmGC backend does
NOT flatten imports — a cross-module `pub fn` is a Wasm import
`<Module>.<fn>`, so stdlib `Http.*` are host imports on wasm (exactly
like the proven `Vscode.httpPostJson`). This splits #225 cleanly:
a host adapter (no compiler-correctness risk, the bulk of the value)
+ the CPS transform (the hard tail, only for user Async fns that
interleave). Does NOT degrade typed-wasm: network I/O is host-mediated
on every wasm target (WASI/JSPI/browser alike); guest logic is still
fully compiled.

PR1 delivers the verified foundation the transform builds on:
- stdlib/Http.affine: `pub extern type Thenable` + `http_request_thenable
  (url, method, body) -> Thenable / { Net, Async }`, mirroring the
  proven httpPostJson/#205 shape. Single portable source surface
  (fetch/get/post) unchanged; this is the wasm-path host-import
  contract, not a second public API.
- tests/codegen/http_thenable_skeleton.affine + harness: wasm e2e
  proving the boundary + #205 protocol end-to-end with a stubbed
  `globalThis.fetch` (no network), pure pass-through (no transform).
- docs/specs/async-on-wasm-cps.adoc: implementation refinement +
  revised 4-PR plan.

Also migrates 5 pre-existing baseline-rot wasm fixtures
(test_record_simple/multiple/multi_field/pattern_record/
tuple_record_array) from retired `{ f: x }` record literals to `#{`
(#218 sweep skipped in-tree wasm fixtures; same as the Deno fixtures
fixed in #226). Record patterns/struct decls correctly left as `{}`.

Gate green: dune test --force 258; full wasm suite green; Http.affine
Deno-AOT clean. Refs #225 #160.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@github-actions
Copy link
Copy Markdown

🔍 Hypatia Security Scan

Findings: 44 issues detected

Severity Count
🔴 Critical 12
🟠 High 21
🟡 Medium 11

⚠️ Action Required: Critical security issues found!

View findings
[
  {
    "reason": "Stray AI.a2ml in root -- use 0-AI-MANIFEST.a2ml only",
    "type": "banned",
    "file": "AI.a2ml",
    "action": "delete",
    "rule_module": "root_hygiene",
    "severity": "high"
  },
  {
    "reason": "Superseded by 0-AI-MANIFEST.a2ml",
    "type": "banned",
    "file": "AI.djot",
    "action": "delete",
    "rule_module": "root_hygiene",
    "severity": "high"
  },
  {
    "reason": "Issue in quality.yml",
    "type": "missing_workflow",
    "file": "quality.yml",
    "action": "create",
    "rule_module": "workflow_audit",
    "severity": "high"
  },
  {
    "reason": "Issue in security-policy.yml",
    "type": "missing_workflow",
    "file": "security-policy.yml",
    "action": "create",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "Action hyperpolymath/standards/.github/workflows/governance-reusable.yml@main needs attention",
    "type": "unpinned_action",
    "file": "governance.yml",
    "action": "pin_sha",
    "rule_module": "workflow_audit",
    "severity": "high"
  },
  {
    "reason": "TypeScript file detected -- banned language",
    "type": "banned_language_file",
    "file": "/home/runner/work/affinescript/affinescript/affinescript-deno-test/example/smoke_driver.ts",
    "action": "flag",
    "rule_module": "cicd_rules",
    "severity": "critical"
  },
  {
    "reason": "TypeScript file detected -- banned language",
    "type": "banned_language_file",
    "file": "/home/runner/work/affinescript/affinescript/affinescript-deno-test/cli.ts",
    "action": "flag",
    "rule_module": "cicd_rules",
    "severity": "critical"
  },
  {
    "reason": "TypeScript file detected -- banned language",
    "type": "banned_language_file",
    "file": "/home/runner/work/affinescript/affinescript/affinescript-deno-test/mod.ts",
    "action": "flag",
    "rule_module": "cicd_rules",
    "severity": "critical"
  },
  {
    "reason": "TypeScript file detected -- banned language",
    "type": "banned_language_file",
    "file": "/home/runner/work/affinescript/affinescript/affinescript-deno-test/lib/compile.ts",
    "action": "flag",
    "rule_module": "cicd_rules",
    "severity": "critical"
  },
  {
    "reason": "TypeScript file detected -- banned language",
    "type": "banned_language_file",
    "file": "/home/runner/work/affinescript/affinescript/affinescript-deno-test/lib/runner.ts",
    "action": "flag",
    "rule_module": "cicd_rules",
    "severity": "critical"
  }
]

Powered by Hypatia Neurosymbolic CI/CD Intelligence

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant