Skip to content

[API] Generate the FFI surface from one schema and remove pointer-only APIs #141

Description

@proggeramlug

Parent: #126

Problem

Bloom exposes hundreds of TypeScript functions across a native-library manifest, shared FFI macros/modules, and per-platform exports. Hand-maintained surfaces drift, and some public APIs still ask Perry callers to supply native pointers or arrays that are not naturally expressible. Examples include collection-heavy operations around scene-node LODs and polygon extrusion, plus legacy pointer-based texture-array APIs (new helper variants improve individual cases but not the underlying contract).

This undermines both simplicity and cross-platform truth: an API can exist in TypeScript yet be missing, stubbed, or practically uncallable on a target.

Outcome

A single declarative FFI schema that generates/validates the manifest, Rust export surface, TypeScript declarations/wrappers, platform capability metadata, and documentation; collection APIs use Perry-safe handles/builders/packed buffers instead of raw pointers.

Schema contract

For every symbol, record:

  • stable FFI name and public TypeScript name/module;
  • parameter/return types, ownership, nullability, units, and error behavior;
  • string encoding and lifetime;
  • availability/capability per platform;
  • whether implementation is shared, platform-specific, unsupported, or deprecated;
  • version/deprecation metadata and doc text/examples;
  • threading/frame-phase constraints where relevant.

Generate deterministic output and provide --check mode that fails if checked-in generated files are stale.

Supported ABI shapes

Keep the primitive ABI small and explicit:

  • scalars and handles;
  • UTF-8 strings with length or a documented Perry string bridge;
  • packed typed buffers referenced by handle + offset/count;
  • builder APIs for complex variable-length structures;
  • structured result/error via numeric code + retrievable message or a proven Perry-safe representation.

Do not expose *const T semantics directly as a friendly TypeScript API.

Collection migration examples

  • extrudePolygon: accept a reusable vec2/float buffer handle or polygon-builder handle plus point count.
  • scene-node LODs: builder or packed records containing model handle, threshold, and options.
  • texture arrays: keep file/texel-friendly helpers; deprecate raw-pointer entry points from public docs once all use cases have safe equivalents.

The wrappers should make one-shot use concise while allowing buffer reuse in hot paths.

Platform parity

  • Shared generated stubs may only represent unsupported with an explicit error/capability, never silent success.
  • tools/validate-ffi.js should validate generated freshness, arity/types, implementation availability, and schema-to-package coverage.
  • Web/watchOS/platform exceptions must live in the schema with reason, not parser allowlists spread across tools.

Acceptance criteria

  • Adding one schema entry produces or checks every required manifest/Rust/TS/doc surface without copying the signature by hand across platforms.
  • CI fails on missing implementation, stale generated output, arity/type mismatch, undocumented stub, or duplicate symbol.
  • Every public collection/pointer API has a Perry-compilable safe path; canonical examples contain no as any pointer workaround.
  • Reusable packed buffers have bounds, overflow, use-after-free, and wrong-element-type tests.
  • String APIs are explicitly UTF-8 and round-trip non-ASCII test data on native and web.
  • Deprecated raw APIs remain source-compatible for one documented window or receive a migration note/version bump.
  • Generated public docs show platform availability and actionable unsupported errors.

Likely files

  • new schema/generator under tools/
  • package.json
  • src/**/*.ts
  • native/shared/src/ffi_core/, native/shared/src/ffi.rs
  • native/*/src/lib.rs
  • tools/validate-ffi.js

Non-goals

  • Generating engine implementation logic.
  • An unconstrained general serialization protocol.
  • Breaking all existing handle-based APIs.

Dependencies

None. Streaming/components/UI should consume the resulting safe patterns rather than invent new ABI shapes.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions