Skip to content

1.0.3

Choose a tag to compare

@dmealing dmealing released this 12 Sep 16:33
· 108 commits to main since this release

All four registries publish: npm 1.0.3 (full lockstep across all 14 @metaobjectsdev/*
publish candidates), Maven Central 8.0.3, PyPI 1.0.3, NuGet 1.0.3. PyPI and NuGet were
last cut at 1.0.1 and adopt the shared minor.patch now current, skipping the number they
sat out (ADR-0035 Amendment 1).

Coordinated PATCH. Every port has changed product code: npm carries FR-023 and the
ADR-0055 reference implementation, Maven / NuGet / PyPI each carry their ADR-0055 port, and
PyPI additionally carries FR-023 Phase 1a. metamodelVersion stays 1.0
expected-registry.json is byte-unchanged since v1.0.2, so no registered vocabulary,
canonical format or wire contract moved.

Added

  • Metadata dependencies (FR-023, Phase 1a — TypeScript + Python). A project can
    now build on a metadata model published by another repository or package instead
    of copy-pasting it:
    • dependencies in .metaobjects/config.json[{ name, path }] today (the
      npm/python transport keys are reserved in the schema and refused by sync
      with ERR_DEPENDENCY_UNRESOLVED, "not supported by this toolchain yet").
    • meta deps sync [<name>…] [--dry-run] / meta deps check / meta deps listsync resolves each dependency's path, validates its manifest and
      artifact hash, re-loads the artifact standalone with core providers, and writes
      the committed snapshot (.metaobjects/deps/<name>/) + lock
      (.metaobjects/deps.lock.json); check compares the currently-installed
      artifact's hash against the lock, read-only; list prints the lock.
    • verify --deps — the same drift comparison as meta deps check, as a
      gated verify subverb (needs the publisher's path reachable, so it is never
      part of the bare-verify default). drifted or unresolved fails with the new
      ERR_DEPENDENCY_UPSTREAM_DRIFT.
    • sharedModelFile() (@metaobjectsdev/codegen-ts) — the publisher-side
      generator: selects a subset of a project's own metadata by the scope
      pattern grammar, closure-checks it, and emits one canonical-JSON artifact +
      metaobjects.pkg.json manifest. Registered and discoverable (meta gen --list), but deliberately not offered by meta eject --list — the
      artifact is a contract whose bytes a cross-port corpus pins and whose hash
      consumers verify, so a user-owned editable copy would invite a silent break.
    • An overlay authoring lint in meta verify — advisory, runs on every
      invocation (no subverb): a top-level (type, resolutionKey) declared in two or
      more collection files (dependency artifacts included) where more than one
      declaration lacks overlay: true. Never fails the build; mute with
      --no-overlay-lint / META_NO_OVERLAY_LINT=1.
    • ERR_DEPENDENCY_PACKAGE_NOT_OWNED — a project may extends or overlay: true a dependency's node freely, but declaring a brand-new top-level node
      into a package a dependency owns is refused by name, naming the fix.
    • New error codes altogether: ERR_DEPENDENCY_UNRESOLVED,
      ERR_DEPENDENCY_MANIFEST_INVALID, ERR_DEPENDENCY_SNAPSHOT_STALE,
      ERR_DEPENDENCY_NODE_COLLISION, ERR_DEPENDENCY_METAMODEL_INCOMPATIBLE,
      ERR_DEPENDENCY_UPSTREAM_DRIFT, ERR_DEPENDENCY_PACKAGE_NOT_OWNED.
    • New exports: declaredTopLevelKeys (@metaobjectsdev/metadata — the raw,
      pre-parse walk the overlay lint and the loader's own overlay-only partition
      share) and serializeSharedDocument (the canonical shared-artifact
      serializer sharedModelFile() targets; Python's serialize_shared_document
      is byte-identical, though Python has no publisher CLI wired to it yet —
      sharedModelFile() itself is TypeScript-only in Phase 1a). FileSource's
      constructor takes an optional { id }, so a dependency's synced snapshot
      loads with dep:<name>/<artifact> provenance instead of reading like a
      local file.
    • Deliberately not built in Phase 1a — see docs/features/metadata-dependencies.md
      ("Deferred"): the npm/python/maven/nuget transports, a local
      co-development override, a usage-aware breaking-change classifier, cross-boundary
      codegen imports (packageBindings), a runtime ObjectManager scope predicate,
      and Java/Kotlin/C# as dependency consumers or publishers (Phase 2).
    • No registered vocabulary changed — metamodelVersion stays 1.0.
      Docs: docs/features/metadata-dependencies.md. Corpus:
      fixtures/dependency-conformance/ (23 cases, TS + Python).

Changed

  • A project that declares scope.include now sees its requirements-ledger
    denominator narrow to that scope — even with zero dependencies.
    The ledger's
    "coverable" object count is computed by
    Collection.inScope, which is matchesScope(fqn, scope) && … regardless of
    whether the project has any dependencies at all. Previously the ledger counted
    every non-abstract entity in the loaded model; a project that scopes its own
    codegen with scope.include now has its ledger coverage counted only over that
    declared scope too. This is a real behavior change for an existing project that
    already declares scope.include and reads its meta verify coverage numbers —
    they may drop even though nothing was deleted.
  • meta verify now runs the overlay authoring lint for EVERY project, not just
    one with dependencies declared.
    runOverlayLintAdvisory() is unconditional —
    it lints collection.files, which for a zero-dependency project is simply its
    own files — so a project with no dependencies at all, but with a top-level
    (type, resolutionKey) redeclared across two or more of its own files (e.g. the
    "Optional layered overlay pattern" — a meta.user.json +
    meta.user.ui.json + meta.user.db.json split), now gets new advisory
    WARN_OVERLAY_IMPLICIT output on meta verify it did not get before this
    release. Advisory only — never fails the build, mute with --no-overlay-lint /
    META_NO_OVERLAY_LINT=1 — but this is a second, real carve-out from "a project
    with no dependencies behaves identically," alongside the ledger-denominator one
    above.
  • Collection (@metaobjectsdev/sdk) gains dependencies, ownFiles,
    fileIds, importedPackages, importedNodes, imported(fqn),
    declaredMigrateScope; inScope and inMigrateScope are now the COMPOSED
    predicates described above (default-exclusion of imported metadata) — byte-for-byte
    identical to their prior behavior for a project with no dependencies and no
    declared scope/migrate.scope.
  • The scope-pattern grammar (compileScope/matchesScope/compilePattern)
    moved from @metaobjectsdev/sdk to @metaobjectsdev/metadata
    — pure,
    browser-safe string code, so @metaobjectsdev/codegen-ts (the sharedModelFile()
    publisher side) can use it without a sdk dependency. sdk re-exports it
    unchanged; existing importers of compileScope/matchesScope from sdk are
    unaffected.
  • scopeExpectedSchema (@metaobjectsdev/migrate-ts) takes a third, optional
    { imported } argument: an imported object the scope does not admit is removed
    from the expected schema before declaredSchemas is computed — so importing a
    table-backed entity can never turn the publisher's other tables into DROP
    candidates, and never proposes creating the imported table.
  • scanRequirements (@metaobjectsdev/cli) takes an optional second argument,
    { coverable } — the predicate the ledger denominator change above threads
    through.

Deprecated

  • @metaobjectsdev/sdk: the v0.3 package.meta.json prototype and workspace discovery
    PackageManifestSchema, PackageManifest, PACKAGE_MANIFEST_FILE, readPackageManifest,
    resolveMetaobjectsPackage, discoverWorkspace, resolveExtendsOrder, packageLabel,
    Workspace, WorkspacePackage. Nothing in the toolchain reads a package.meta.json;
    cross-repo metadata sharing is being built as FR-023 (metadata dependencies). They still work
    and are removed in 2.0.

Fixed

  • overlay: true no longer depends on file order — the loader applies overlays in a
    deferred pass
    (ADR-0055). The
    parser used to resolve an overlay against the accumulating tree the instant it met it, so the
    declaration it re-opens had to have been parsed already. extends has never had that
    constraint: it is deferred to a pass that runs after every source is read. Overlays now work
    the same way — every source is parsed first, applying only plain declarations, and the queued
    overlays are applied afterwards, before super-resolution.

    What this fixes, concretely:

    • A MIXED file — one carrying both plain and overlay: true top-level declarations —
      failed ERR_OVERLAY_NO_TARGET whenever its target lived in a later-sorted file. The #160
      partition that was meant to prevent this tested whole FILES (is every declaration in this
      file an overlay?
      ), so a mixed file was classified as a base and never moved. That partition
      is now deleted in every port; nothing needs it.
    • An overlay declared above its base in the same file now loads, in JSON and in YAML.
    • Python diverged silently rather than failing. Its loader merges post-parse, so its
      partition chose which node absorbed which: with an overlay-first root as the accumulator
      the base was merged INTO the overlay, producing children [ov, id] where every other port
      produces [id, ov] — a difference the byte-gated canonical contract cares about. Folding
      every root through one matcher also fixes two declarations of one name in a SINGLE Python
      file staying two disconnected siblings.
    • Java reported ERR_UNKNOWN. Its throw carried no structured code, so the code was
      scraped from the message and found nothing. It now emits ERR_OVERLAY_NO_TARGET with the
      same message wording as every other port.
    • One bad overlay no longer discards its whole source. The eager throw abandoned the
      entire document, losing every sibling declaration and cascading into ERR_UNRESOLVED_SUPER;
      each overlay is now applied independently and every failure is reported.

    Two behaviour changes ride along, both pinned by the corpus. ERR_OVERLAY_NO_TARGET's
    envelope moves from the parse-time format: "json" / "yaml" shape to ADR-0009 FR5d's
    format: "resolved", carrying the same files/jsonPath plus referrer and target. And
    because the queue is unconditional — an overlay is deferred even when its target is already
    present, since applying it eagerly "when the base happens to be there" is the retry-on-miss
    variant ADR-0055 rejects — an overlay now lands after every plain declaration, including an
    unflagged redeclaration in a later file that previously landed after it.
    Reaching that
    corner requires an unflagged second declaration of a node, which meta verify already reports
    as WARN_OVERLAY_IMPLICIT.

    metamodelVersion does not move (it stays 1.0): no registered vocabulary, canonical
    format or wire contract changed. This is additive on the metadata axis — input that failed now
    loads, and nothing that loaded stops loading. Shipped in all five ports (TypeScript, C#, Java,
    Kotlin via the JVM loader, Python), gated by nine new conformance fixtures.