@metreeca/blue - v0.10.0
    Preparing search index...

    Module resource

    Resource shapes and factories.

    Defines ResourceShape and the resource, property, id, and type factories used to declare the expected structure of linked data resources. Shapes carry property definitions, cardinality constraints, IRI mappings, and inheritance, and drive both runtime validation and compile-time type inference through Content. Use model to extract the deeply typed retrieval template stored on a resource shape.

    Important

    Resource shapes are closed: validated resources may only contain entries explicitly defined in the shape. Any additional entries will cause validation to fail.

    Important

    All IRI values in validated resources must be absolute. When decoding client input, relative references may be auto-resolved using the base option in decodeResource or decodeSelection.

    Defining Resource Shapes

    Combine property definitions with value ranges to define resource structures:

    import { required, optional, repeatable } from '@metreeca/blue/value';
    import { resource, id } from '@metreeca/blue/resource';
    import { string } from '@metreeca/blue/string';
    import { integer } from '@metreeca/blue/number';
    import { boolean } from '@metreeca/blue/boolean';

    const Product = resource({
    id: id(),
    name: required(string({ model: "name", minLength: 1 })),
    price: required(integer({ minInclusive: 0 })),
    available: optional(boolean()),
    tags: repeatable(string())
    });

    Properties and Ranges

    Ranges define cardinality constraints for property values:

    import { required, optional, multiple, repeatable, cardinality } from '@metreeca/blue/value';
    import { string } from '@metreeca/blue/string';
    import { resource } from '@metreeca/blue/resource';

    const Shape = resource({
    name: required(string()), // 1..1
    alias: optional(string()), // 0..1
    tags: repeatable(string()), // 1..*
    notes: multiple(string()), // 0..*
    codes: cardinality(2, 5)(string()) // 2..5
    });

    Naked ranges are automatically wrapped in a property; use the explicit property factory when IRI mappings or labels are needed:

    import { required } from '@metreeca/blue/value';
    import { string } from '@metreeca/blue/string';
    import { resource, property } from '@metreeca/blue/resource';
    import { createNamespace } from '@metreeca/core/resource';

    const schema = createNamespace("http://schema.org/");

    const Person = resource({
    name: property({ forward: schema }, required(string()))
    });

    Resource References and Embedding

    Resource entries link to other resources in two ways. A reference wrapper links to a standalone resource, an independently identified and managed entity. A direct shape inclusion defines an embedded resource, a nested object with no independent identity, created and managed together with its parent.

    Note

    An embedded resource shape may not carry an id entry: embedded resources have no independent identity, so a nested resource state bearing an identifier is rejected during state validation rather than at shape construction, since an id-bearing embedded range is indistinguishable from an expanded captive reference until a state is checked against it. A type entry is accepted and validated in both state and template retrieval.

    import { required, optional } from '@metreeca/blue/value';
    import { string } from '@metreeca/blue/string';
    import { number } from '@metreeca/blue/number';
    import { resource, id } from '@metreeca/blue/resource';
    import { reference } from '@metreeca/blue/reference';

    const Rating = resource({
    average: required(number({ minInclusive: 0, maxInclusive: 5 })),
    reviews: required(number({ minInclusive: 0 }))
    });

    const Vendor = resource({
    id: id(),
    name: required(string())
    });

    const Product = resource({
    id: id(),
    name: required(string()),
    rating: optional(Rating), // embedded
    vendor: required(reference(Vendor)) // standalone
    });

    Self-referential shapes use lazy factories to defer resolution and avoid infinite recursion at definition time:

    function Category() {
    return resource({
    id: id(),
    name: required(string()),
    parent: optional(reference(Category))
    });
    }

    Retrieval Form

    In a retrieval template, an embedded resource property accepts only a nested resource template, a nested object validated against this shape and subject to the template validator's depth budget. Bare IRI strings are not accepted, since an embedded resource has no independent identifier of its own. See validate for the full form comparison and reference!ReferenceShape for the companion standalone form.

    Property Mappings versus Foreign References

    The forward and reverse mappings on a property control how property values are persisted — both write actual property mappings. The foreign flag on a reference shape is an independent concept: a read-only view over mappings owned by another property that does not write any mappings on insert. During resource validation, foreign reference entries are rejected; during template validation they are accepted for data retrieval.

    Embedded versus Captive Resources

    Embedded resources have no independent identity or lifecycle and are always managed as part of their parent. An embedded resource shape may not carry an id entry: the rejection is enforced during state validation rather than at shape construction, since an id-bearing embedded range is indistinguishable from an expanded captive reference until a resource state is checked against it. A type entry is accepted and validated in both state and template retrieval. Embedded resources are defined by directly including a resource shape without a reference wrapper.

    Captive resources, identified by the captive flag, have independent identity and lifecycle but cannot outlive the source resource and are automatically cascade-removed when it is deleted.

    Inheritance

    Extend parent shapes to inherit entries and constraints:

    import { required } from '@metreeca/blue/value';
    import { string } from '@metreeca/blue/string';
    import { integer } from '@metreeca/blue/number';
    import { resource, id } from '@metreeca/blue/resource';

    const NamedEntity = resource({
    id: id(),
    name: required(string({ model: "name", minLength: 1 }))
    });

    const Employee = resource({ extends: NamedEntity }, {
    department: required(string()),
    salary: required(integer({ minInclusive: 0 }))
    });
    Important

    Constraints are enforced conjunctively: when a child shape overrides an inherited property, values must satisfy both the child's constraints and all inherited constraints. Overrides can restrict inherited constraints but never relax them.

    Warning

    Constraints that can be expressed in the type system — such as non-empty set requirements on in, hasValue, languageIn, and validators — are enforced at compile time and not re-validated at runtime.

    Narrowing union slots

    When a parent declares a union-typed slot, an extending shape may drop variants and tighten the variants it keeps, but never add new ones. Narrowing takes one of two forms:

    1. Single-variant narrowing (Form 1) — the child supplies a non-union value shape that narrows exactly one of the parent's variants (by kind, only-tightening constraints, a matching datatype for string / number, and a matching target shape for reference or a subtype class for resource). The merged slot becomes a bare value shape; consumers see the variant's plain model rather than the union's variant-keyed model.
    2. Union subsetting (Form 2) — the child supplies a smaller union whose variants each narrow a distinct parent variant. The pairing is order-independent and injective; surviving variants are merged and unpaired parent variants are dropped.
    import { required } from '@metreeca/blue/value';
    import { union } from '@metreeca/blue/union';
    import { string } from '@metreeca/blue/string';
    import { integer } from '@metreeca/blue/number';
    import { resource } from '@metreeca/blue/resource';

    const Entity = resource({
    code: required(union(string(), integer()))
    });

    // Form 1 — narrows the slot to a bare string
    const Vendor = resource({ extends: Entity }, {
    code: required(string({ model: "ABC", pattern: "^[A-Z]" }))
    });

    // Form 2 — keeps the union but drops the string variant wholesale
    const Numbered = resource({ extends: Entity }, {
    code: required(union(integer()))
    });

    The merged union's model re-indexes contiguously from 0; consumers must key off the shape's own model, not assume positional alignment with an ancestor. See UnionShape for the full inheritance contract.

    Polymorphic Properties

    Use union for entries accepting multiple value types. Variants are supplied as positional arguments and act as mutually exclusive alternatives (sh:xone): a state value singles out exactly one variant (sh:xone) on write, while a model placeholder matches at least one by kind (sh:or) on read. Cardinality constraints belong on the enclosing SetShape, not on individual variants. At runtime, values are stored directly with no variant wrapping:

    import { optional, required } from '@metreeca/blue/value';
    import { union } from '@metreeca/blue/union';
    import { string } from '@metreeca/blue/string';
    import { resource } from '@metreeca/blue/resource';
    import { reference } from '@metreeca/blue/reference';

    const PostalAddress = resource({
    id: id(),
    street: required(string()),
    city: required(string())
    });

    const Contact = resource({
    address: optional(union(
    string(),
    reference(PostalAddress)
    ))
    });

    Either variant is accepted at the same property position:

    { "address": "123 Main St" }

    { "address": "https://data.example.com/addresses/456" }

    Custom Validators

    Implement custom resource-level constraints as validators reporting keyed Trace records, or undefined when the resource passes:

    import type { Validator } from '@metreeca/blue';
    import { optional } from '@metreeca/blue/value';
    import { date } from '@metreeca/blue/string';
    import { integer } from '@metreeca/blue/number';
    import { resource } from '@metreeca/blue/resource';

    interface Product { minPrice?: number; maxPrice?: number; startDate?: string; endDate?: string }

    const checkProduct: Validator<Product> = value => {

    const priceIssue = value.minPrice !== undefined && value.maxPrice !== undefined
    && value.minPrice > value.maxPrice;

    const dateIssue = value.startDate !== undefined && value.endDate !== undefined
    && value.startDate > value.endDate;

    return priceIssue || dateIssue ? {
    ...(priceIssue ? { minPrice: "minPrice must not exceed maxPrice" } : {}),
    ...(dateIssue ? { startDate: "startDate must not follow endDate" } : {})
    } : undefined;

    };

    const Product = resource({ validators: [checkProduct] }, {
    minPrice: optional(integer()),
    maxPrice: optional(integer()),
    startDate: optional(date()),
    endDate: optional(date())
    });

    Variables

    defaultNamespace

    Default application namespace for property IRI resolution (app:/#).

    Interfaces

    ResourceShape

    Shape definition for linked data resources.

    ResourceConstraints

    Constraints for resource shape factories.

    Id

    Shape definition for the resource identifier property.

    Type

    Shape definition for the resource type property.

    Property

    Shape definition for a resource property.

    PropertyConstraints

    Constraints for property shape factories.

    Type Aliases

    Entry

    A ResourceShape entry.

    Members

    Member definitions for a ResourceShape factory.

    Member

    Member definition accepted by the resource factory.

    Prototype

    Assembles the per-key template prototype backing a resource's model.

    Override

    Narrows locally-declared entries against an inherited template, flagging incompatible overrides.

    Narrowings

    Expands an inherited slot type into the union of its valid narrowings.

    Slot

    Projects a Member to the template-side value type it contributes to Prototype.

    Inheritance

    Resolves the inherited template contributed by a resource's extends clause.

    Composition

    Composes the resource model from local entries and inherited template.

    Content

    Projects a Member to its state-side runtime value type.

    Range

    Extracts the SetShape range carried by a Member.

    Relaxed

    Relaxes the keys of an object type into optional keys wherever the value type admits absence.

    Declared

    Strips index signatures from a record type, keeping only explicitly declared entries.

    Intersected

    Collapses a union type into the intersection of its members.

    Merged

    Collapses an intersection of object types into a single object type.

    Functions

    getShapeClass

    Resolves a shape's own class.

    getShapeClasses

    Resolves a shape's inherited classes.

    getShapeId

    Resolves a shape's identifier field name.

    getShapeType

    Resolves a shape's type field name.

    getShapeProperties

    Resolves a shape's entries.

    resource

    Creates resource shapes.

    id

    Creates a marker for the resource identifier property.

    type

    Creates a marker for the resource type property.

    property

    Creates property shapes.