UNPKG

domain-objects

Version:

A simple, convenient way to represent domain objects, leverage domain knowledge, and add runtime validation in your code base.

85 lines 5.17 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.getContract = void 0; const helpful_errors_1 = require("helpful-errors"); const validate_1 = require("../instantiation/validate/validate"); const getContractRef_1 = require("./getContractRef"); const getKind_1 = require("./getKind"); /** * .what = maps a `.nested` declaration to nested dobj names only (string, or string[] for polymorphic choices) * .why = the contract carries nested identity by name, never the constructor objects (name-only per vision) * .note = handles both the single-constructor and array-of-constructors forms, like `hydrateNestedDomainObjects` */ const toNestedNames = (nested) => { const entries = Object.entries(nested).map(([key, declaration]) => [ key, Array.isArray(declaration) ? declaration.map((NestedClass) => NestedClass.name) : declaration.name, ]); return Object.fromEntries(entries); }; /** * .what = per-class memo of the stamped contract, keyed by the dobj class constructor * .why = `.meta()` returns a fresh schema on each call; cache per-class so repeated `.contract` * access is idempotent (same instance back), per the vision's pit-of-success contract */ const contractByClass = new WeakMap(); /** * .what = returns a domain object's `.contract`: its zod `schema` stamped with identity + key metadata * .why = * - lets a domain object's identity survive `z.toJSONSchema()` as an `x-domain-object` pragma * - so a cross-service consumer can name, de-dupe, and reconstruct the dobj from the wire * - mirrors how `serialize` stamps `_dobj` onto its string output * .note = `schema` validates; `contract` identifies. the contract is the schema that knows its own name + keys. * the result is memoized per-class, so repeated access returns the same stamped instance (idempotent). * .note = the returned contract also carries a `.ref(by)` method — `.contract.ref('primary')` returns * the schema-level *reference* to this dobj by key (an `x-domain-object-ref` pragma; see getContractRef). */ const getContract = (dobj) => { // return the memoized contract if this class was already stamped (idempotent per-class) const contractMemoized = contractByClass.get(dobj); if (contractMemoized) return contractMemoized; // fail fast if no schema is declared; a contract has no shape to identify without one const { schema } = dobj; if (!schema) throw new helpful_errors_1.ConstraintError(`${dobj.name}.contract requires a static schema. declare \`static schema\` (zod) on ${dobj.name} to use .contract`, { domainObject: dobj.name }); // fail fast if the schema is not zod; only zod can carry the json-schema identity pragma if (!(0, validate_1.isZodSchema)(schema)) throw new helpful_errors_1.ConstraintError(`${dobj.name}.contract requires a zod schema; joi/yup cannot carry json-schema identity. keep .schema for validation, but .contract needs zod`, { domainObject: dobj.name }); // assemble the x-domain-object pragma from the declared statics (omit absent fields) const pragma = { name: dobj.name, kind: (0, getKind_1.getKind)(dobj), // the true subclass (entity/literal/event/object), from the class marker ...(dobj.primary ? { primary: dobj.primary } : {}), ...(dobj.unique ? { unique: dobj.unique } : {}), ...(dobj.alias ? { alias: dobj.alias } : {}), ...(dobj.nested ? { nested: toNestedNames(dobj.nested) } : {}), }; // stamp the pragma via zod's `.meta()` registry (returns a fresh schema, the author's is // untouched), then augment it with the `.ref(by)` accessor — a function property, absent from // z.toJSONSchema() output (which walks zod's internal `_zod.def`, not own-enumerable keys). // // `.ref` is hung as a NON-enumerable, non-writable, non-configurable own property, the way // withImmute attaches `.clone` (withImmute.ts). non-enumerable so `Object.keys(X.contract)` / // `{ ...X.contract }` / a log never leak the fn where a pure zod schema is expected — the raw // schema stays indistinguishable from an un-augmented one to every enumerable-key consumer. // // as-cast boundary (`rule.forbid.as-cast` exception): `.meta()` is typed to return a zod schema, // which cannot express "this schema now also carries our `.ref` method". the cast asserts the // shape defineProperty just produced (a zod schema + `.ref`). removal path: when zod exposes a // typed schema-augmentation api, or the lib returns a wrapper instead of the schema itself. const stamped = schema.meta({ 'x-domain-object': pragma }); const contract = Object.defineProperty(stamped, 'ref', { enumerable: false, configurable: false, writable: false, value: (by) => (0, getContractRef_1.getContractRef)(dobj, by), }); // memoize per-class so the next access returns this same instance (idempotent, vision pit-of-success) contractByClass.set(dobj, contract); return contract; }; exports.getContract = getContract; //# sourceMappingURL=getContract.js.map