@thomasfosterau/legation 0.0.0-stage → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +2054 -0
- package/LICENSE +21 -0
- package/README.md +350 -2
- package/dist/MemoryBackend-BziXwfcs.d.ts +4884 -0
- package/dist/MemoryBackend-BziXwfcs.d.ts.map +1 -0
- package/dist/MemoryBackend-DxP8t4ge.js +9477 -0
- package/dist/MemoryBackend-DxP8t4ge.js.map +1 -0
- package/dist/index-CPPfp0F4.d.ts +399 -0
- package/dist/index-CPPfp0F4.d.ts.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +3 -0
- package/dist/internal-CxXRlFK9.d.ts +2362 -0
- package/dist/internal-CxXRlFK9.d.ts.map +1 -0
- package/dist/internal-JyoQMlfK.js +3758 -0
- package/dist/internal-JyoQMlfK.js.map +1 -0
- package/dist/internal.d.ts +3 -0
- package/dist/internal.js +3 -0
- package/dist/rolldown-runtime-D8jQpDG7.js +12 -0
- package/dist/sql/index.d.ts +294 -0
- package/dist/sql/index.d.ts.map +1 -0
- package/dist/sql/index.js +969 -0
- package/dist/sql/index.js.map +1 -0
- package/dist/src-FWXpu_vP.js +1082 -0
- package/dist/src-FWXpu_vP.js.map +1 -0
- package/package.json +69 -3
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"src-FWXpu_vP.js","names":["make","FilterPolicy.resolve","FilterPolicy.kindOf","Subset.parseOrder","Collection.make","registry","Collection.registry","Mutator.make","Mutator.registry","Subset.hash","MirrorProtocol.relatedKey","Identity.transitionOf","Query.tryPlan","Coverage.ofPlan","verdictOf","Coverage.requestedOf","Coverage.verdictOf","Query.run","Query.plan","Query.runPlanVia","Coverage.snapshotOf","Query.runRows","Query.edgeSelects","Query.applyWindows","plan","make","ClientStore.ClientStore","ClientStore.makeMemory","RpcTransport.fromOrigin","Transport.ClientTransport","Transport.offline","ClientEngine.make","ClientView.fromEngine","Mirror.make","ClientEngineModule.resyncAttachedEngines","Subset.apply","Query.plan","ClientEngineModule.make","Mutator.registry","Cvr.makeMemoryStore","InvalidationBus.makeInProcess","SyncServer.makeWith","Authorization.AuthPolicy","Cvr.CvrStore","InvalidationBus.InvalidationBus","Authorization.IdentityResolver","Query.QueryResolver","Authorization.SyncMiddleware"],"sources":["../src/Node.ts","../src/Edge.ts","../src/CrudMutation.ts","../src/Client.ts","../src/Hydration.ts","../src/Server.ts"],"sourcesContent":["/**\n * Node\n *\n * A typed resource (ADR-0022): scalar attributes plus {@link Edge}s to\n * other nodes. The Node definition is the single source of truth the rest\n * of the system derives from — its row schema (the identity attribute + its\n * attributes + the foreign-key fields its `one` edges declare), the\n * {@link Collection} the engine materializes, the joins and sync windows its\n * edges imply, and the fan-out **derivable set** (ADR-0023: `one`-edge keys\n * + declared `hintKeys`), and the **filterable set** (ADR-0028, #154: the\n * fields a client may filter or sort by, with their operators and kinds).\n *\n * Ids are branded per node type, so a `products` id cannot be passed where\n * a `categories` id is expected; `one`-edge fields carry the **target**\n * type's brand.\n *\n * A node's **identity attribute** is the attribute supplying its row id. It\n * defaults to a synthetic branded `id` added to the row, and a domain with\n * natural keys names one of its own attributes instead (`id: \"username\"`) —\n * so the key is stored once, not duplicated into an `id` nothing keeps equal\n * to it. Everything downstream reads the declared name: the derived\n * collection's id extractor, both ends of every edge's join (each resolved\n * from its own node, so the two ends may differ), and the filterable set a\n * client's `where` / `order` is admitted against. Row ids are strings on the\n * wire and in CVR bookkeeping either way (ADR-0008) — this names which\n * attribute supplies the string, nothing more.\n *\n * Edges reference targets by name; {@link registry} closes the graph and\n * validates it (targets exist, inverses resolve to a matching `one` edge),\n * then derives the engine's `CollectionRegistry` — so the existing engine,\n * `Related` joins, and auth filters consume Node definitions unchanged\n * while later ADR-0022 program steps move onto them directly.\n */\n\nimport * as Arr from \"effect/Array\";\nimport type * as Brand from \"effect/Brand\";\nimport * as Schema from \"effect/Schema\";\nimport * as Collection from \"./Collection.js\";\nimport * as Edge from \"./Edge.js\";\nimport { DuplicateNameError, FilterDeclarationError, UnknownCollectionError } from \"./Errors.js\";\nimport * as FilterPolicy from \"./FilterPolicy.js\";\nimport * as Subset from \"./Subset.js\";\n\n/** The branded id schema for a node type: `string & Brand<\"<type>/id\">`. */\nexport interface Id<Type extends string> extends Schema.brand<Schema.String, `${Type}/id`> {}\n\n/** Create the branded id schema for a node type. */\nexport const Id = <const Type extends string>(type: Type): Id<Type> =>\n // SAFETY: effect 4.0.0 types `Schema.brand`'s parameter as\n // `B & EnsureSingleBrandKey<B>`, which TypeScript cannot resolve while `Type`\n // is generic. The guard only checks that the identifier is one concrete\n // (non-union, non-wide) string and is `unknown` for the literal ids callers\n // pass; the cast only satisfies it, the value is the same template string,\n // and the public `Id<Type>` signature is untouched.\n Schema.String.pipe(\n Schema.brand<`${Type}/id`>(`${type}/id` as Parameters<typeof Schema.brand<`${Type}/id`>>[0])\n );\n\n/** The foreign-key fields a node's `one` edges add to its row schema. */\ntype ViaFields<Edges extends Record<string, Edge.Edge>> = {\n readonly [\n K in keyof Edges as Edges[K] extends Edge.One<string, infer Via> ? Via : never\n ]: Edges[K] extends Edge.One<infer Target, string> ? Id<Target> : never;\n};\n\n/**\n * The field names of a node's row: its identity attribute — the synthetic\n * `id` unless the definition names one of its own attributes — plus its\n * attributes and its `one`-edge keys.\n */\nexport type FieldName<\n Fields extends Schema.Struct.Fields,\n Edges extends Record<string, Edge.Edge>,\n IdField extends string | undefined = undefined,\n> =\n | (IdField extends string ? never : \"id\")\n | (keyof Fields & string)\n | (keyof ViaFields<Edges> & string);\n\n/**\n * The row schema of a node: a synthetic branded `id` plus the attributes and\n * `one`-edge keys, or — when the definition names an attribute as its\n * identity — the attributes and `one`-edge keys alone, the identity among\n * them.\n */\nexport type Row<\n Type extends string,\n Fields extends Schema.Struct.Fields,\n Edges extends Record<string, Edge.Edge>,\n IdField extends string | undefined = undefined,\n> = IdField extends string\n ? Schema.Struct<Fields & ViaFields<Edges>>\n : Schema.Struct<{ readonly id: Id<Type> } & Fields & ViaFields<Edges>>;\n\n/** A node definition. */\nexport interface Node<\n Type extends string = string,\n Fields extends Schema.Struct.Fields = Schema.Struct.Fields,\n Edges extends Record<string, Edge.Edge> = Record<string, Edge.Edge>,\n IdField extends string | undefined = string | undefined,\n> {\n readonly type: Type;\n /**\n * The attribute supplying the row id: the declared one (`make`'s `id`\n * option), or `\"id\"` — the synthetic branded attribute added to the row\n * when a node names none. Every place the graph joins, filters, sorts or\n * keys by identity reads this name, never the literal `\"id\"`.\n */\n readonly id: IdField extends string ? IdField : \"id\";\n readonly attributes: Fields;\n readonly edges: Edges;\n /**\n * Extra fields eligible for fine fan-out topics beyond the `one`-edge\n * keys (ADR-0023) — typically the auth-scoping key when it is not\n * already an edge.\n */\n readonly hintKeys: ReadonlyArray<string>;\n /**\n * Fields eligible for secondary indexing (issue #113), carried through to\n * the derived {@link Collection}'s `indexes`. See\n * {@link Collection.Collection.indexes}.\n */\n readonly indexes: ReadonlyArray<string>;\n /**\n * The **filterable set** (ADR-0028 rule d, #154): every field a client may\n * filter by, with its operators and literal kind. The identity attribute\n * and every `one`-edge `via` field are filterable by `eq` / `in` (kind\n * `text`) by default; everything else is declared via `make`'s\n * `filterable` option. Carried to the derived {@link Collection} and\n * enforced by `Authorization.authorize` before evaluation.\n */\n readonly filterable: Readonly<Record<string, FilterPolicy.FilterableField>>;\n /**\n * The fields a client may order by (#154); always includes the identity\n * attribute (ADR-0026's tie-break).\n */\n readonly sortable: ReadonlyArray<string>;\n /** The branded id schema for this node type. */\n readonly Id: Id<Type>;\n /**\n * The row schema: `attributes & one-edge key fields`, with a branded `id`\n * added in front unless an attribute is declared as the identity.\n */\n readonly row: Row<Type, Fields, Edges, IdField>;\n}\n\n/**\n * A node definition with its type parameters erased.\n *\n * `Id` is bounded rather than erased to `Id<string>`: in Effect 4.0.0 brands\n * are type-only, so a concrete `brand<String, \"todos/id\">` no longer assigns\n * to a template-literal brand. The bound keeps it a string-encoded branded id\n * schema, whose decoded type is `string & Brand<string>`.\n */\nexport type AnyNode = Omit<Node<string, Schema.Struct.Fields, Record<string, Edge.Edge>>, \"Id\"> & {\n readonly Id: Schema.Codec<string & Brand.Brand<string>, string>;\n};\n\n/** The decoded row type of a node. */\nexport type RowOf<N extends AnyNode> = N[\"row\"][\"Type\"];\n\n/** Define a node type. */\nexport const make = <\n const Type extends string,\n const Fields extends Schema.Struct.Fields,\n // oxlint-disable-next-line effect/avoid-object-type -- public type parameter default of exported `Node.make`; `Record<string, never>` would change `keyof Edges` from `never` to `string` for nodes declared without edges.\n const Edges extends Record<string, Edge.Edge> = {},\n const IdField extends (keyof Fields & string) | undefined = undefined,\n>(\n type: Type,\n options: {\n /**\n * The attribute supplying the row id — a **natural key**\n * (`id: \"username\"`), which must be one of this node's own string\n * attributes. Omitted, the row gets a synthetic branded `id` attribute\n * and everything behaves exactly as it did before a node could name its\n * identity, so a domain with surrogate keys declares nothing.\n *\n * Row ids stay strings on the wire and in CVR bookkeeping either way\n * (ADR-0008); this only names which attribute supplies the string.\n */\n readonly id?: IdField;\n readonly attributes: Fields;\n readonly edges?: Edges;\n readonly hintKeys?: ReadonlyArray<string>;\n readonly indexes?: ReadonlyArray<string>;\n /**\n * The fields a client may filter by, beyond the identity / `one`-edge key\n * defaults: the operators alone (the literal kind is derived from the\n * attribute's schema — `FilterPolicy.kindOf`), or `{ operators, kind }`\n * where derivation cannot tell (an `instant` stored as text). A key\n * field may be listed to widen its default operators.\n */\n readonly filterable?: {\n readonly [K in FieldName<Fields, Edges, IdField>]?: FilterPolicy.FilterableInput;\n };\n /** The fields a client may order by, beyond the identity attribute. */\n readonly sortable?: ReadonlyArray<FieldName<Fields, Edges, IdField>>;\n }\n): Node<Type, Fields, Edges, IdField> => {\n const edges = options.edges ?? ({} as Edges);\n const id = Id(type);\n const identity = options.id ?? \"id\";\n if (options.id !== undefined) assertTextIdentity(type, options.id, options.attributes);\n const viaFields: Record<string, Schema.Top> = {};\n Arr.forEach(Object.values(edges), (edge) => {\n if (edge.kind === \"one\") viaFields[edge.via] = Id(edge.target);\n });\n const declaration = FilterPolicy.resolve({\n collection: type,\n attributes: options.attributes,\n identity,\n keyFields: [identity, ...Object.keys(viaFields)],\n filterable: options.filterable as Readonly<Record<string, FilterPolicy.FilterableInput>>,\n sortable: options.sortable,\n });\n const row = Schema.Struct({\n // A declared identity is already one of the attributes; only a node\n // without one gets the synthetic branded `id`.\n ...(options.id === undefined ? { id } : {}),\n ...options.attributes,\n ...viaFields,\n }) as Node<Type, Fields, Edges, IdField>[\"row\"];\n return {\n type,\n id: identity as Node<Type, Fields, Edges, IdField>[\"id\"],\n attributes: options.attributes,\n edges,\n hintKeys: options.hintKeys ?? [],\n indexes: options.indexes ?? [],\n filterable: declaration.filterable,\n sortable: declaration.sortable,\n Id: id,\n row,\n };\n};\n\n/**\n * A declared identity attribute must carry the row id, and a row id is a\n * string on the wire (ADR-0008) — so an attribute whose literal kind is\n * derivably something else is a definition defect, not a runtime surprise at\n * the filter boundary. A kind that cannot be derived at all passes: the\n * declaration is taken at its word, exactly as `filterable` does.\n */\nconst assertTextIdentity = (\n type: string,\n identity: string,\n attributes: Schema.Struct.Fields\n): void => {\n const attribute = attributes[identity];\n const kind = attribute === undefined ? undefined : FilterPolicy.kindOf(attribute);\n if (kind === undefined || kind === \"text\") return;\n throw new FilterDeclarationError({\n collection: type,\n field: identity,\n message: `Node \"${type}\" declares \"${identity}\" as its identity attribute, but its literal kind is \"${kind}\"; a row id is a string`,\n });\n};\n\n/**\n * The fields of a node eligible for fine fan-out topics (ADR-0023): its\n * `one`-edge keys plus declared `hintKeys`. Declared on the node, so\n * topic registration and write-derivation cannot disagree.\n */\nexport const derivableSet = (node: AnyNode): ReadonlySet<string> => {\n const fields = new Set<string>(node.hintKeys);\n Arr.forEach(Object.values(node.edges), (edge) => {\n if (edge.kind === \"one\") fields.add(edge.via);\n });\n return fields;\n};\n\n/** A closed, validated node graph plus the engine registry derived from it. */\nexport interface NodeRegistry {\n readonly nodes: ReadonlyMap<string, AnyNode>;\n /** The engine's collection registry, derived edge-for-edge. */\n readonly collections: Collection.CollectionRegistry;\n}\n\n/** Resolve a collection-valued edge's key field from its inverse. */\nconst inverseVia = (\n node: AnyNode,\n name: string,\n edge: Edge.Many | Edge.Paginated,\n nodes: ReadonlyMap<string, AnyNode>\n): string => {\n const target = nodes.get(edge.target);\n if (target === undefined) {\n throw new UnknownCollectionError({\n from: node.type,\n relationship: name,\n collection: edge.target,\n });\n }\n const inverse = target.edges[edge.inverse];\n if (inverse === undefined || inverse.kind !== \"one\" || inverse.target !== node.type) {\n throw new UnknownCollectionError({\n from: node.type,\n relationship: `${name} (inverse \"${edge.inverse}\" is not a one-edge back to ${node.type})`,\n collection: edge.target,\n });\n }\n return inverse.via;\n};\n\n/**\n * The identity attribute of an edge's target — resolved per-node, because\n * the two ends of one edge may name different identities (`comments.author →\n * users.username` while `comments.article → articles.slug`).\n */\nconst identityOf = (\n node: AnyNode,\n name: string,\n target: string,\n nodes: ReadonlyMap<string, AnyNode>\n): string => {\n const resolved = nodes.get(target);\n if (resolved === undefined) {\n throw new UnknownCollectionError({ from: node.type, relationship: name, collection: target });\n }\n return resolved.id;\n};\n\n/** Derive the engine collection for one node (registry context required). */\nconst toCollection = (\n node: AnyNode,\n nodes: ReadonlyMap<string, AnyNode>\n): Collection.AnyCollection => {\n const relationships: Array<Collection.Relationship> = [];\n Arr.forEach(Object.entries(node.edges), ([name, edge]) => {\n if (edge.kind === \"one\") {\n relationships.push({\n name,\n kind: \"one\",\n collection: edge.target,\n localField: edge.via,\n // The join matches the TARGET's identity attribute.\n foreignField: identityOf(node, name, edge.target, nodes),\n });\n } else {\n relationships.push({\n name,\n kind: edge.kind,\n collection: edge.target,\n // The parent side of a dependent collection is this node's identity.\n localField: node.id,\n foreignField: inverseVia(node, name, edge, nodes),\n ...(edge.kind === \"paginated\" ? { order: Subset.parseOrder(edge.order) } : {}),\n });\n }\n });\n return Collection.make({\n name: node.type,\n // oxlint-disable-next-line effect/avoid-any, effect/no-chained-type-assertions -- `AnyNode` erases fields to the open `Schema.Struct.Fields`, whose services are `unknown`; `Collection.make` needs a service-free codec, which node rows are by construction but the exported types cannot state.\n schema: node.row as unknown as Schema.Codec<unknown, unknown>,\n id: (value) => (value as Record<string, string>)[node.id] ?? \"\",\n identity: node.id,\n relationships,\n ...(Arr.isReadonlyArrayNonEmpty(node.indexes) ? { indexes: node.indexes } : {}),\n // The filterable set travels like `indexes` (#154): the server enforces\n // it from the collection alone.\n filterable: node.filterable,\n sortable: node.sortable,\n });\n};\n\n/**\n * Every field of a node's row: its identity attribute, its attributes, its\n * `one`-edge keys. A declared identity is one of the attributes already; an\n * undeclared one is the synthetic `id`.\n */\nconst rowFields = (node: AnyNode): ReadonlySet<string> => {\n const fields = new Set<string>([node.id, ...Object.keys(node.attributes)]);\n Arr.forEach(Object.values(node.edges), (edge) => {\n if (edge.kind === \"one\") fields.add(edge.via);\n });\n return fields;\n};\n\n/**\n * A paginated edge's declared `order` is what its per-parent windows sort\n * the **target** by (ADR-0024), so those fields join the target's\n * `sortable` here — declared on the node graph, so window registration and\n * the filterable set cannot disagree (as `one`-edge keys are `eq` / `in`).\n * An order field the target's row does not carry is a definition defect.\n */\nconst withEdgeOrders = (node: AnyNode, definitions: ReadonlyArray<AnyNode>): AnyNode => {\n const sortable = new Set<string>(node.sortable);\n const fields = rowFields(node);\n Arr.forEach(definitions, (source) =>\n Arr.forEach(Object.entries(source.edges), ([name, edge]) => {\n if (edge.kind !== \"paginated\" || edge.target !== node.type) return;\n Arr.forEach(Subset.parseOrder(edge.order), (term) => {\n if (!fields.has(term.field)) {\n throw new FilterDeclarationError({\n collection: node.type,\n field: term.field,\n message: `Paginated edge \"${name}\" on ${source.type} orders \"${node.type}\" by \"${term.field}\", which is not a field of its row`,\n });\n }\n sortable.add(term.field);\n });\n })\n );\n return sortable.size === node.sortable.length ? node : { ...node, sortable: [...sortable] };\n};\n\n/**\n * Close and validate the node graph: unique types, every edge target\n * present, every collection-valued edge's `inverse` resolving to a `one`\n * edge back to the declaring node, every paginated edge's order over fields\n * its target carries. Returns the graph — each node's `sortable` widened by\n * the paginated edges ordering it — plus the derived engine\n * `CollectionRegistry`.\n */\nexport const registry = (definitions: ReadonlyArray<AnyNode>): NodeRegistry => {\n const declared = new Map<string, AnyNode>();\n Arr.forEach(definitions, (node) => {\n if (declared.has(node.type)) {\n throw new DuplicateNameError({ kind: \"collection\", name: node.type });\n }\n declared.set(node.type, node);\n });\n Arr.forEach(definitions, (node) => {\n Arr.forEach(Object.entries(node.edges), ([name, edge]) => {\n if (!declared.has(edge.target)) {\n throw new UnknownCollectionError({\n from: node.type,\n relationship: name,\n collection: edge.target,\n });\n }\n if (edge.kind !== \"one\") inverseVia(node, name, edge, declared);\n });\n });\n const nodes = new Map<string, AnyNode>();\n Arr.forEach(definitions, (node) => {\n nodes.set(node.type, withEdgeOrders(node, definitions));\n });\n const collections = Collection.registry(\n [...nodes.values()].map((node) => toCollection(node, nodes))\n );\n return { nodes, collections };\n};\n","/**\n * Edge\n *\n * A node's relationship to other nodes (ADR-0022): `one`, `many`, or\n * `paginated`. An edge is a **view over one foreign key, never storage** —\n * a collection-valued edge from `products` to `reviews` IS the dependent\n * collection `reviews where productId = <parent id>`. The key is declared\n * once, on the child (`Edge.one(\"products\", { via: \"productId\" })`); the\n * parent's collection-valued edge names the **inverse** rather than\n * restating the mapping, so the two directions cannot drift.\n *\n * Targets are referenced **by name** and closed through the node registry\n * (`Node.registry`), so mutually-referencing definitions carry no\n * type-level cycle.\n *\n * The same key powers the join (`Related`), the sync window (an equality\n * subset), the auth scope (ADR-0015), and the fan-out topic (ADR-0023's\n * derivable set).\n */\n\n/** A reference to a single foreign node, held as a foreign-key field. */\nexport interface One<Target extends string = string, Via extends string = string> {\n readonly kind: \"one\";\n readonly target: Target;\n /** The field on THIS node's rows holding the foreign id. */\n readonly via: Via;\n}\n\n/** The dependent collection of target nodes whose inverse edge points here. */\nexport interface Many<Target extends string = string> {\n readonly kind: \"many\";\n readonly target: Target;\n /** The name of the `one` edge on the target that holds the key. */\n readonly inverse: string;\n}\n\n/**\n * Like {@link Many}, but windowed: consumers page through it with cursors,\n * and each `(parent, edge, window)` is its own sync unit (ADR-0022).\n * `order` is part of the edge's identity — connection cursors need a\n * stable order, so it is not a per-query choice. Tokens are field names,\n * `-` prefixed for descending (`\"-createdAt\"`).\n */\nexport interface Paginated<Target extends string = string> {\n readonly kind: \"paginated\";\n readonly target: Target;\n /** The name of the `one` edge on the target that holds the key. */\n readonly inverse: string;\n readonly order: ReadonlyArray<string>;\n}\n\n/**\n * An edge on a node: a {@link One} reference, a {@link Many} collection or a {@link Paginated} one.\n */\nexport type Edge = One | Many | Paginated;\n\n/** Declare a reference to a single `target` node, stored in field `via`. */\nexport const one = <const Target extends string, const Via extends string>(\n target: Target,\n options: { readonly via: Via }\n): One<Target, Via> => ({ kind: \"one\", target, via: options.via });\n\n/** Declare the dependent collection of `target` nodes (unbounded). */\nexport const many = <const Target extends string>(\n target: Target,\n options: { readonly inverse: string }\n): Many<Target> => ({ kind: \"many\", target, inverse: options.inverse });\n\n/** Declare the dependent collection of `target` nodes, paged by cursor. */\nexport const paginated = <const Target extends string>(\n target: Target,\n options: { readonly inverse: string; readonly order: ReadonlyArray<string> }\n): Paginated<Target> => ({\n kind: \"paginated\",\n target,\n inverse: options.inverse,\n order: options.order,\n});\n","/**\n * CrudMutation\n *\n * The **graph-derived CRUD mutations** (ADR-0030 §5): for every node a graph\n * declares, a `{type}/create`, `{type}/update` and `{type}/delete`\n * {@link Mutation}, whose args are in the **graph row form** — the flat row\n * a node-graph replica stores, attributes and `one`-edge `via` keys side by\n * side — with the client (optimistic) handler that writes it. They are what a\n * plain write means when nobody said otherwise: the JSON:API façade's default\n * intent mapping journals a reflected `POST` / `PATCH` / `DELETE` as one of\n * them (`@thomasfosterau/legation-jsonapi`'s `JsonApiFacade`).\n *\n * Args, for a node `reviews` with an edge `product: Edge.one(\"products\", { via: \"productId\" })`:\n *\n * - `reviews/create`: `{ type: \"reviews\", id, attributes }`, `attributes`\n * holding the row's fields (`productId` among them). The handler puts the\n * row `{ ...attributes, [node.id]: id }`.\n * - `reviews/update`: the same shape, with only the fields being set. The\n * handler merges them over the current row and is a **no-op when the row\n * is absent**, so a patch journaled behind a create that the server later\n * rejects leaves no phantom row behind (ADR-0030 §2).\n * - `reviews/delete`: `{ type: \"reviews\", id }`. The handler deletes the row\n * (a no-op when it is absent).\n *\n * The args schemas are permissive (`attributes` is a record of unknown\n * values), so the encoded and decoded forms coincide and a JSON body can be\n * journaled as decoded. Attribute values are not validated here: the server's\n * handler under the same name is the authority (ADR-0008), and a value it\n * refuses surfaces as a permanent rejection.\n *\n * **Not `Derive.ts`'s mutations.** `@thomasfosterau/legation-jsonapi`'s\n * resource-derived CRUD mutators share these names but store a different row\n * shape (`JsonApiRow`: attributes flat, `relationships` nested, for the\n * client-direct stack with no node graph), and their `update` puts a partial\n * row when the row is absent. An engine registers one family or the other,\n * never both. These are for a replica built from a node graph.\n *\n * The server half is the app's: register a backend handler under each name\n * the client registers, applying the app's own write rules.\n */\n\nimport * as Effect from \"effect/Effect\";\nimport * as Schema from \"effect/Schema\";\nimport * as Mutator from \"./Mutator.js\";\nimport type * as Node from \"./Node.js\";\n\n/** The three operations a graph-derived mutation performs. */\nexport type Operation = \"create\" | \"update\" | \"delete\";\n\n/**\n * Which operations to derive, per node type. A type absent from the map gets\n * none; an absent map derives all three for every node.\n */\nexport type Writes = Readonly<Record<string, ReadonlyArray<Operation>>>;\n\n/** The mutation name for a type and an operation: `\"{type}/{operation}\"`. */\nexport const name = (type: string, operation: Operation): string => `${type}/${operation}`;\n\n/** The args of a `{type}/create` or `{type}/update`: the row's fields as a record, keyed by its id. */\nexport const WriteArgs = Schema.Struct({\n type: Schema.String,\n id: Schema.String,\n attributes: Schema.Record(Schema.String, Schema.Unknown),\n});\n\n/** The decoded (and encoded) args of a create or an update. */\nexport type WriteArgs = typeof WriteArgs.Type;\n\n/** The args of a `{type}/delete`. */\nexport const DeleteArgs = Schema.Struct({ type: Schema.String, id: Schema.String });\n\n/** The decoded (and encoded) args of a delete. */\nexport type DeleteArgs = typeof DeleteArgs.Type;\n\n/** One derived mutation: its definition, its client handler, and what it does to which type. */\nexport interface Entry {\n readonly mutator: Mutator.AnyMutator;\n readonly local: Mutator.LocalHandler<any>;\n readonly type: string;\n readonly operation: Operation;\n}\n\n/** Options for {@link entries} and {@link registry}. */\nexport interface Options {\n /** Which operations to derive per type; default all three for every node. */\n readonly writes?: Writes | undefined;\n}\n\nconst derivesFor = (writes: Writes | undefined, type: string, operation: Operation): boolean =>\n writes === undefined || (writes[type] ?? []).includes(operation);\n\n/** The derived mutations of one node, filtered by `writes`. */\nconst nodeEntries = (node: Node.AnyNode, writes: Writes | undefined): ReadonlyArray<Entry> => {\n const { type } = node;\n const identity = node.id;\n const entries: Array<Entry> = [];\n if (derivesFor(writes, type, \"create\")) {\n entries.push({\n type,\n operation: \"create\",\n mutator: Mutator.make(name(type, \"create\"), WriteArgs),\n local: (args: WriteArgs, writer) =>\n writer.put(type, args.id, { ...args.attributes, [identity]: args.id }),\n });\n }\n if (derivesFor(writes, type, \"update\")) {\n entries.push({\n type,\n operation: \"update\",\n mutator: Mutator.make(name(type, \"update\"), WriteArgs),\n local: (args: WriteArgs, writer) =>\n Effect.flatMap(writer.get(type, args.id), (current) =>\n current === undefined\n ? Effect.void\n : writer.put(type, args.id, {\n // SAFETY: a row of a node collection is the node's row struct, an object.\n ...(current as Record<string, unknown>),\n ...args.attributes,\n [identity]: args.id,\n })\n ),\n });\n }\n if (derivesFor(writes, type, \"delete\")) {\n entries.push({\n type,\n operation: \"delete\",\n mutator: Mutator.make(name(type, \"delete\"), DeleteArgs),\n local: (args: DeleteArgs, writer) => writer.del(type, args.id),\n });\n }\n return entries;\n};\n\n/**\n * The graph-derived CRUD mutations of every node in the graph (those `writes`\n * selects), as registry entries an app spreads beside its own named\n * mutations: `Mutation.registry([...mine, ...CrudMutation.entries(nodes)])`.\n *\n * @example\n * ```ts\n * import * as Schema from \"effect/Schema\";\n * import { CrudMutation, Edge, Mutation, Node } from \"@thomasfosterau/legation\";\n *\n * const products = Node.make(\"products\", { attributes: { name: Schema.String } });\n * const reviews = Node.make(\"reviews\", {\n * attributes: { rating: Schema.Number },\n * edges: { product: Edge.one(\"products\", { via: \"productId\" }) },\n * });\n * const nodes = Node.registry([products, reviews]);\n *\n * const registry = Mutation.registry(\n * CrudMutation.entries(nodes, { writes: { reviews: [\"create\", \"delete\"] } })\n * );\n * console.log([...registry.mutators.keys()]); // [\"reviews/create\", \"reviews/delete\"]\n * ```\n */\nexport const entries = (nodes: Node.NodeRegistry, options?: Options): ReadonlyArray<Entry> =>\n [...nodes.nodes.values()].flatMap((node) => nodeEntries(node, options?.writes));\n\n/** The graph-derived CRUD mutations as a registry of their own. Duplicate names cannot occur. */\nexport const registry = (nodes: Node.NodeRegistry, options?: Options): Mutator.MutatorRegistry =>\n Mutator.registry(entries(nodes, options));\n","/**\n * Client\n *\n * The one-per-thread object (ADR-0022): it runs many {@link Query} reads\n * and mutations, and **where a read is answered is configuration, never a\n * concept the UI sees**:\n *\n * - {@link make} owns the engine in this thread (store + transport come\n * from context; the sync loop starts automatically — the class of bug\n * where an app forgets `runSync` ends here). Starting the loop is **not**\n * the same as auto-pushing, though: on its own it only wires the baseline\n * triggers (pull on poke, push+pull on reconnect), so a mutation journaled\n * on an already-connected client sits until one of those fires or the app\n * calls `push` explicitly. Auto-push is `syncLoop.pushDebounce` — opt in\n * with it if writes should reach the server without anything else\n * happening first (the examples use `\"300 millis\"`).\n * - {@link connect} joins an engine hosted elsewhere — a service worker,\n * an Electron main process, a daemon — over a `MirrorChannel`; the\n * machinery behind it (the mirror) is internal vocabulary.\n *\n * Reads are declarative ({@link Query.Read}); **observing subscribes**: a\n * live read registers its windows for the stream's lifetime through a\n * per-window ledger (one voice per window, however many observers), and\n * `subscribe` pins a read's windows — root and, with `include`, edge —\n * beyond any observer. A subscription can also be named by its JSON:API\n * URL (`subscribe(url)`, ADR-0029 section 2) when the Client is built with\n * {@link UrlReads}; the canonical URL is then the subscription's identity\n * for devtools and the façade. Includes resolve through the view's\n * auto-subscribing relational read — host-side on a connected engine,\n * locally on an owned one — with the plan's edge windows applied.\n *\n * **Reads have three tiers and the UI sees none of them** (ADR-0025):\n * `query` answers from the replica when a subscription in this Client's\n * ledger **covers** the read — conjunct subsumption with strict windows\n * (ADR-0029 section 3, `Coverage`); otherwise it falls through to the server — point-in-time,\n * auth-filtered, no CVR, results never entering the replica — degrading\n * back to the local answer when no transport read is available or the\n * server is unreachable (offline-first: local is always an answer).\n * `observe` never falls through: observing subscribes, which IS the\n * upgrade from a one-shot fallthrough to a live, replica-backed read.\n */\n\nimport * as Arr from \"effect/Array\";\nimport * as Context from \"effect/Context\";\nimport * as Effect from \"effect/Effect\";\nimport * as Exit from \"effect/Exit\";\nimport * as Option from \"effect/Option\";\nimport * as P from \"effect/Predicate\";\nimport * as R from \"effect/Record\";\nimport * as Ref from \"effect/Ref\";\nimport * as Scope from \"effect/Scope\";\nimport * as Semaphore from \"effect/Semaphore\";\nimport * as Stream from \"effect/Stream\";\nimport * as ClientEngine from \"./ClientEngine.js\";\nimport * as ClientStore from \"./ClientStore.js\";\nimport * as RpcTransport from \"./RpcTransport.js\";\nimport * as Transport from \"./Transport.js\";\nimport * as ClientView from \"./ClientView.js\";\nimport * as Coverage from \"./Coverage.js\";\nimport { QueryError, StoreError, TransportError } from \"./Errors.js\";\nimport * as Identity from \"./Identity.js\";\nimport type * as Mutator from \"./Mutator.js\";\nimport type { Mutation } from \"./Protocol.js\";\nimport type * as MutationState from \"./MutationState.js\";\nimport type * as Node from \"./Node.js\";\nimport * as Query from \"./Query.js\";\nimport type * as QueryModule from \"./Query.js\";\nimport type * as Related from \"./Related.js\";\nimport * as Subset from \"./Subset.js\";\nimport type { MirrorChannel } from \"./MirrorChannel.js\";\nimport * as Mirror from \"./Mirror.js\";\nimport * as MirrorProtocol from \"./MirrorProtocol.js\";\n\n/** The rows a read resolves to: node values with their included edges. */\nexport type ResolvedRow = Related.RelatedRow;\n\n/**\n * The one-per-thread object an app reads and writes through (ADR-0022): queries, observed reads,\n * pins, mutations and the identity boundary, whether the engine is owned or hosted elsewhere.\n */\nexport interface Client {\n /** The node graph this Client reads (bindings compile plans against it). */\n readonly nodes: Node.NodeRegistry;\n /** Run a read once against the local replica (no subscription). */\n readonly query: (\n read: QueryModule.Read\n ) => Effect.Effect<ReadonlyArray<ResolvedRow>, ClientView.ClientViewError>;\n /**\n * Observe a read: current rows now, re-emitting as they change. The\n * read's windows are registered for the stream's lifetime — observing\n * IS subscribing.\n */\n readonly observe: (read: QueryModule.Read) => Stream.Stream<ReadonlyArray<ResolvedRow>>;\n /**\n * Pin a read beyond any observer's lifetime: its root window and, for a\n * read with `include`, its edge windows — the same relational\n * registration `observe` runs (ADR-0029 section 2), so a subscription\n * that covers a read really holds the foreign rows rule 4 promises. A\n * string is a JSON:API URL, parsed and canonicalised through the\n * Client's {@link UrlReads}; `subscribe(url)` and `subscribe(read)` of\n * one query share one ledger entry. Pins are counted: release one per\n * subscribe. Fails with a `QueryError` for a URL the Client cannot\n * parse, or any URL when it was built without `urls`; nothing is\n * registered then.\n */\n readonly subscribe: (\n read: QueryModule.Read | string\n ) => Effect.Effect<void, ClientView.ClientViewError>;\n readonly unsubscribe: (\n read: QueryModule.Read | string\n ) => Effect.Effect<void, ClientView.ClientViewError>;\n /**\n * The canonical URLs of this Client's explicit pins, in registration\n * order — the subscription identities of ADR-0029 section 2, for\n * devtools. A pin whose read has no URL form (a per-edge window) is not\n * listed; a Client built without {@link UrlReads} lists nothing.\n */\n readonly subscriptions: Effect.Effect<ReadonlyArray<string>>;\n /**\n * Which subscription in this Client's ledger covers a read, if any — the\n * check {@link Client.query} makes before answering locally, exposed for\n * devtools (ADR-0029 section 4: \"which subscription covered the request\n * is recorded beside it\").\n *\n * `options.snapshot`, when given, is the root rows the caller already read for\n * this read (with the subset they are of): the verdict is judged over those rows\n * instead of a fresh read, so a caller that answers from the same rows judges\n * exactly what it answers (#426). A snapshot of any other subset is not used; the\n * verdict then reads its root as usual. Omit it to judge a fresh read.\n */\n readonly coverageOf: (\n read: QueryModule.Read,\n options?: Coverage.JudgeOptions\n ) => Effect.Effect<Coverage.Verdict>;\n /** Journal a mutation and apply it optimistically; resolves on journal. */\n readonly mutate: (\n mutation: string,\n args: unknown\n ) => Effect.Effect<void, ClientView.ClientViewError>;\n /** Like {@link Client.mutate}, resolving with the authoritative outcome. */\n readonly mutateWithAck: (\n mutation: string,\n args: unknown\n ) => Effect.Effect<ClientEngine.MutationOutcome, ClientView.ClientViewError>;\n /**\n * The client half of an enhanced form submit (ADR-0030 #215 §4): apply the\n * Mutation optimistically under the form client's id, without journaling it\n * (the form action delivers it), and settle the returned submission on the\n * action's receipt. An acknowledgement parks the effect in the unpulled\n * tail until the pull that covers the form client releases it; a rejection\n * drops it. Over a connected host (`connect`) the host's engine holds the\n * entry and every mirror sees its overlay (#536); a host that predates that\n * answers with a `TransportError`, as does a view that carries no form\n * submit at all.\n */\n readonly beginFormMutation: (\n form: ClientEngine.FormClient,\n mutation: string,\n args: unknown\n ) => Effect.Effect<ClientEngine.FormSubmission, ClientView.ClientViewError>;\n /**\n * The keyed mutation-state map (#116) — TanStack Query's\n * `useMutationState` analogue: every journaled mutation's current\n * lifecycle state, current value now then every fold. Works identically\n * over an owned engine and a connected host (ADR-0021).\n */\n readonly mutationStates: Stream.Stream<MutationState.MutationStateMap>;\n /**\n * Adopt an identity, scoping the durable replica to it (#212). **The\n * invariant: a durable replica belongs to exactly one resolved identity,\n * and an identity change is a reset, not a rebase** (CONTEXT.md, \"Identity\n * boundary\").\n *\n * Call this whenever the app's session changes — signing in, signing out,\n * switching accounts — passing the opaque {@link Identity.Fingerprint} of\n * the new principal (never PII, never a credential; see the type's docs).\n * The engine applies one rule, the same one the store applies at boot:\n *\n * - **Unchanged** — same principal; the replica is untouched.\n * - **Upgrade** — {@link Identity.anonymous} → signed in: the warm replica\n * survives, with its subscriptions and journal. The rows a public\n * session synced are a subset of what the new identity may read, so\n * discarding here would be a pointless cold start on the commonest\n * first-sign-in path.\n * - **Reset** — every other change, sign-out included: confirmed rows, the\n * pull cookie and every subscription this Client holds are dropped, and\n * unpushed journal entries are **quarantined** under the superseded\n * fingerprint — never discarded (that is the user's data) and never\n * inherited by the new session (that is a forged write). They become\n * pushable again only if that identity signs back in; until then only\n * {@link Client.discardQuarantine} can destroy them.\n *\n * A reset does not end live {@link Client.observe} streams that are already\n * open — it empties what they observe and releases their windows, so such a\n * stream keeps reading a replica that no longer syncs it. That is the\n * conservative reading of \"a reset, not a rebase\": the superseded identity's\n * windows are not silently re-registered for the new principal. An app\n * should tear down and re-open its reads across a sign-out, which is what\n * remounting the signed-in UI does anyway.\n *\n * Fails with a `StoreError` on a Client built with {@link connect}: the\n * durable replica belongs to the engine's host, so reset the host's Client.\n */\n readonly reset: (\n identity: Identity.Fingerprint\n ) => Effect.Effect<ClientEngine.ResetOutcome, ClientView.ClientViewError>;\n /** The fingerprint this Client's replica is stamped with, if any. */\n readonly identity: Effect.Effect<Identity.Fingerprint | undefined, ClientView.ClientViewError>;\n /**\n * Fingerprints currently holding quarantined write intents — what an app\n * lists to offer \"you have unsent changes from a previous session\".\n */\n readonly quarantines: Effect.Effect<\n ReadonlyArray<Identity.Fingerprint>,\n ClientView.ClientViewError\n >;\n /** The write intents quarantined under a fingerprint, in causal order. */\n readonly quarantinedMutations: (\n identity: Identity.Fingerprint\n ) => Effect.Effect<ReadonlyArray<Mutation>, ClientView.ClientViewError>;\n /**\n * Destroy a quarantine. The only path that discards a superseded\n * identity's unpushed writes, and never a side effect of a reset or a boot\n * — an engine that silently threw away a user's unsynced work would be\n * trading one data-loss bug for another.\n */\n readonly discardQuarantine: (\n identity: Identity.Fingerprint\n ) => Effect.Effect<void, ClientView.ClientViewError>;\n /**\n * The underlying view — the escape hatch while the ADR-0022 program\n * lands (named-query registration, bindings plumbing).\n */\n readonly view: ClientView.ClientView;\n}\n\n/**\n * URL-keyed reads (ADR-0029 section 2): the bridge between a JSON:API URL\n * and a {@link Query.Read}, and the canonical URL that is a subscription's\n * identity. `@thomasfosterau/legation` does not know the URL grammar — effect-jsonapi\n * owns it, behind `@thomasfosterau/legation-jsonapi` (ADR-0006) — so a Client takes this\n * as an option: `JsonApiQuery.urlReads(nodes)` from `@thomasfosterau/legation-jsonapi`\n * builds it from the node graph. A Client built without it fails a URL\n * call with a `QueryError`, never a defect.\n */\nexport interface UrlReads {\n /** Parse a URL into a read; a `QueryError` names the offending parameter. */\n readonly parse: (url: string) => Effect.Effect<QueryModule.Read, QueryError>;\n /**\n * The canonical URL of a read — one string per plan, whatever spelling\n * the read used. Fails for a read with no URL form (a per-edge window,\n * an `Exists`): such a pin has no URL identity and is not listed by\n * {@link Client.subscriptions}.\n */\n readonly canonical: (read: QueryModule.Read) => Effect.Effect<string, QueryError>;\n}\n\n/** Options shared by every way of building a Client. */\nexport interface ViewOptions {\n /** URL-keyed reads; without them `subscribe(url)` fails with a `QueryError`. */\n readonly urls?: UrlReads;\n}\n\n/** One ledger entry: who holds a registration, and what it covers (with its URL identity when known). */\ninterface Interest extends Coverage.Subscription {\n /** Live observers (a live read without `include`). */\n readonly observers: number;\n /** Explicit pins, counted so release is symmetric with subscribe. */\n readonly pins: number;\n}\n\n/** What a subscribe or unsubscribe resolved its input to. */\ninterface Target {\n readonly registration: Coverage.Registration;\n readonly url: string | undefined;\n}\n\ntype Kind = \"observer\" | \"explicit\";\n\n/** One more unit of `kind` on a count that tracks `tracked`. */\nconst oneMore = (count: number | undefined, kind: Kind, tracked: Kind): number =>\n (count ?? 0) + (kind === tracked ? 1 : 0);\n\n/** One unit of `kind` fewer on a count that tracks `tracked`, never below zero. */\nconst oneFewer = (count: number, kind: Kind, tracked: Kind): number =>\n Math.max(0, count - (kind === tracked ? 1 : 0));\n\n/** Nobody holds the entry any more. */\nconst unheld = (interest: Interest): boolean => interest.observers === 0 && interest.pins === 0;\n\n/**\n * An entry with one unit of `kind` fewer. The canonical URL is a pin's\n * identity, so it goes with the last pin even while observers keep the\n * entry: {@link Client.coverageOf} names only what {@link Client.subscriptions}\n * lists.\n */\nconst oneFewerOn = (current: Interest, kind: Kind): Interest => {\n const { url, ...rest } = current;\n const pins = oneFewer(current.pins, kind, \"explicit\");\n return {\n ...rest,\n observers: oneFewer(current.observers, kind, \"observer\"),\n pins,\n ...(pins > 0 && url !== undefined ? { url } : {}),\n };\n};\n\n/**\n * The ledger key of a registration: the root window's hash alone, or with\n * its edge selects — the same key the mirror protocol gives a relational\n * registration — so a read with `include` is its own entry beside the\n * plain root, and each entry is what coverage compares against.\n */\nconst keyOf = (registration: Coverage.Registration): string =>\n Arr.isReadonlyArrayEmpty(registration.edges)\n ? Subset.hash(registration.root)\n : MirrorProtocol.relatedKey(\n registration.root.collection,\n registration.root,\n registration.edges\n );\n\nconst noUrlReads = (url: string): QueryError =>\n new QueryError({\n message:\n \"Client.subscribe(url): this Client has no URL reads; build it with `urls` \" +\n \"(`JsonApiQuery.urlReads(nodes)` from @thomasfosterau/legation-jsonapi) to subscribe by URL\",\n query: url,\n });\n\n/**\n * Build a Client over any view. The window ledger lives here, so however\n * many observers a window has — and however the view refcounts — the\n * Client speaks with one voice per window: subscribe on first interest,\n * unsubscribe when the last observer ends and no explicit pin remains.\n */\nexport const fromView = (\n nodes: Node.NodeRegistry,\n view: ClientView.ClientView,\n options: ViewOptions = {}\n): Client => {\n const urls = options.urls;\n // One ledger per Client: registrations keyed by `keyOf`, each holding one\n // unit on its root window, whose view registration is made once however\n // many registrations share it. Edge windows are held per registration in\n // a scope of their own.\n const ledger = Ref.makeUnsafe(new Map<string, Interest>());\n const roots = Ref.makeUnsafe(new Map<string, number>());\n const edgeScopes = new Map<string, Scope.Closeable>();\n // The ledger update and the view registration it decides are one\n // critical section: an unsubscribe interleaved with an in-flight\n // subscribe of the same read would otherwise release before the\n // registration exists, leaking it.\n const lock = Semaphore.makeUnsafe(1);\n\n // ─── Root windows: one voice per window across every registration ────────\n\n const holdRoot = (root: Subset.Subset): Effect.Effect<void> =>\n Ref.modify(roots, (map) => {\n const key = Subset.hash(root);\n const count = map.get(key) ?? 0;\n return [count === 0, new Map(map).set(key, count + 1)] as const;\n }).pipe(\n Effect.flatMap((first) => (first ? view.subscribe(root).pipe(Effect.orDie) : Effect.void))\n );\n\n const dropRoot = (root: Subset.Subset): Effect.Effect<void> =>\n Ref.modify(roots, (map) => {\n const key = Subset.hash(root);\n const count = map.get(key) ?? 0;\n const next = new Map(map);\n if (count <= 1) next.delete(key);\n else next.set(key, count - 1);\n return [count === 1, next] as const;\n }).pipe(\n Effect.flatMap((last) => (last ? view.unsubscribe(root).pipe(Effect.ignore) : Effect.void))\n );\n\n // ─── Edge windows: the relational registration, held open per entry ──────\n\n // The registration `observe`'s relational read makes (ADR-0029 section 2)\n // without its join — host-side over a connected engine, the reconcile\n // loop over an owned one — kept open in a scope of its own so the foreign\n // windows keep tracking the linkage until the entry is released.\n const openEdges = (key: string, registration: Coverage.Registration): Effect.Effect<void> =>\n Effect.gen(function* () {\n const scope = yield* Scope.make();\n const { root, edges } = registration;\n yield* view\n .pinEdges(nodes.collections, root.collection, root, edges)\n .pipe(Scope.provide(scope));\n edgeScopes.set(key, scope);\n });\n\n const closeEdges = (key: string): Effect.Effect<void> =>\n Effect.suspend(() => {\n const scope = edgeScopes.get(key);\n edgeScopes.delete(key);\n return scope === undefined ? Effect.void : Scope.close(scope, Exit.void);\n });\n\n // ─── The ledger ──────────────────────────────────────────────────────────\n\n const register = (key: string, registration: Coverage.Registration): Effect.Effect<void> =>\n holdRoot(registration.root).pipe(\n Effect.andThen(\n Arr.isReadonlyArrayEmpty(registration.edges) ? Effect.void : openEdges(key, registration)\n )\n );\n\n const unregister = (key: string, registration: Coverage.Registration): Effect.Effect<void> =>\n closeEdges(key).pipe(Effect.andThen(dropRoot(registration.root)));\n\n /** Add one unit of interest; registers with the view when it is the first. */\n const acquire = (\n registration: Coverage.Registration,\n kind: Kind,\n url?: string\n ): Effect.Effect<void> => Semaphore.withPermit(lock, acquireUnlocked(registration, kind, url));\n\n const acquireUnlocked = (\n registration: Coverage.Registration,\n kind: Kind,\n url: string | undefined\n ): Effect.Effect<void> =>\n Ref.modify(ledger, (map) => {\n const key = keyOf(registration);\n const current = map.get(key);\n const known = url ?? current?.url;\n const next = new Map(map).set(key, {\n root: registration.root,\n edges: registration.edges,\n observers: oneMore(current?.observers, kind, \"observer\"),\n pins: oneMore(current?.pins, kind, \"explicit\"),\n ...(known === undefined ? {} : { url: known }),\n });\n return [current === undefined ? key : undefined, next] as const;\n }).pipe(\n Effect.flatMap((key) => (key === undefined ? Effect.void : register(key, registration)))\n );\n\n /** Drop one unit of interest; releases from the view when it was the last. */\n const release = (registration: Coverage.Registration, kind: Kind): Effect.Effect<void> =>\n Semaphore.withPermit(lock, releaseUnlocked(registration, kind));\n\n const releaseUnlocked = (registration: Coverage.Registration, kind: Kind): Effect.Effect<void> =>\n Ref.modify(ledger, (map) => {\n const key = keyOf(registration);\n const current = map.get(key);\n if (current === undefined) return [undefined, map] as const;\n const updated = oneFewerOn(current, kind);\n const next = new Map(map);\n if (unheld(updated)) {\n next.delete(key);\n return [key, next] as const;\n }\n next.set(key, updated);\n return [undefined, next] as const;\n }).pipe(\n Effect.flatMap((key) => (key === undefined ? Effect.void : unregister(key, registration)))\n );\n\n // ─── The identity boundary ───────────────────────────────────────────────\n\n /**\n * Drop every registration this Client holds, releasing each through the\n * view exactly as the last observer or pin would. A reset's subscriptions\n * are as much the superseded identity's as its rows: a ledger entry left\n * behind would keep `coverageOf` claiming the new principal's reads are\n * answerable locally, and would re-pull the old windows on the next sync.\n * Roots are deduped the way {@link dropRoot} counts them, so the view hears\n * one release per window however many entries shared it.\n */\n const clearLedger: Effect.Effect<void> = Semaphore.withPermit(\n lock,\n Effect.gen(function* () {\n const entries = yield* Ref.getAndSet(ledger, new Map<string, Interest>());\n yield* Ref.set(roots, new Map<string, number>());\n const released = new Set<string>();\n yield* Effect.forEach(\n entries,\n ([key, interest]) =>\n Effect.gen(function* () {\n yield* closeEdges(key);\n const root = Subset.hash(interest.root);\n if (released.has(root)) return;\n released.add(root);\n yield* view.unsubscribe(interest.root).pipe(Effect.ignore);\n }),\n { concurrency: 1, discard: true }\n );\n })\n );\n\n const noBoundary = <A>(): Effect.Effect<A, StoreError> =>\n Effect.fail(\n new StoreError({\n message:\n \"Client.reset: this Client is connected to an engine hosted elsewhere \" +\n \"(Client.connect), which owns the durable replica. Reset the Client that \" +\n \"owns the engine — in the service worker or other host — instead.\",\n })\n );\n\n const boundary = view.identityBoundary;\n\n // The ledger goes first: releasing through the view keeps the counted\n // subscription facade's holds in step, where clearing the engine's\n // registrations underneath it would strand them.\n const reset: Client[\"reset\"] = (identity) =>\n boundary === undefined\n ? noBoundary()\n : Effect.gen(function* () {\n const transition = Identity.transitionOf(\n (yield* boundary.current) ?? Identity.unstamped,\n identity\n );\n if (transition === \"Reset\") yield* clearLedger;\n return yield* boundary.reset(identity);\n });\n\n // ─── Reads by value or by URL ────────────────────────────────────────────\n\n const targetOfUrl = (url: string): Effect.Effect<Target, QueryError> =>\n urls === undefined\n ? Effect.fail(noUrlReads(url))\n : Effect.gen(function* () {\n const read = yield* urls.parse(url);\n const plan = yield* Query.tryPlan(nodes, read);\n return { registration: Coverage.ofPlan(plan), url: yield* urls.canonical(read) };\n });\n\n // A read given by value keeps its URL identity when it has one; a read\n // with no URL form is pinned all the same, without one.\n const targetOfRead = (read: QueryModule.Read): Effect.Effect<Target, QueryError> =>\n Effect.gen(function* () {\n const plan = yield* Query.tryPlan(nodes, read);\n const url =\n urls === undefined\n ? undefined\n : Option.getOrUndefined(yield* Effect.option(urls.canonical(read)));\n return { registration: Coverage.ofPlan(plan), url };\n });\n\n const targetOf = (input: QueryModule.Read | string): Effect.Effect<Target, QueryError> =>\n P.isString(input) ? targetOfUrl(input) : targetOfRead(input);\n\n // ─── Coverage ────────────────────────────────────────────────────────────\n\n // One rule, one scan: the first pulled ledger entry (in registration order)\n // that covers the read, under `Coverage`. The read's root rows are read from\n // the view, so the edge windows it links to are the ones judged (#426). An\n // entry whose windows the read needs and the replica does not hold yet covers\n // nothing: the read goes to the server. A failed read of the rows is uncovered.\n const verdictOf = (\n plan: Query.Plan,\n rowsOf: Coverage.RowsOf<ClientView.ClientViewError> = view.query\n ): Effect.Effect<Coverage.Verdict> =>\n Coverage.requestedOf(nodes.collections, plan, rowsOf).pipe(\n Effect.flatMap((requested) =>\n Ref.get(ledger).pipe(\n Effect.flatMap((interests) =>\n Effect.forEach(interests.values(), (interest) =>\n view\n .pulled(keyOf(interest))\n .pipe(Effect.map((pulled): Coverage.Entry => ({ ...interest, pulled })))\n )\n ),\n Effect.map((entries) => Coverage.verdictOf(entries, requested))\n )\n ),\n Effect.catchTag([\"MutatorError\", \"StoreError\", \"TransportError\", \"QueryError\"], () =>\n Effect.succeed<Coverage.Verdict>({ _tag: \"Uncovered\" })\n )\n );\n\n // The three-tier read (ADR-0025): replica when a ledger subscription\n // covers the read (ADR-0029); server fallthrough otherwise\n // (`Query.runPlanVia` over the transport's read — the same plan and join\n // the derived REST handlers run server-side — with the plan compiled once,\n // for the coverage verdict and the run); local again when fallthrough is\n // unavailable or fails (offline-first — local is always an answer).\n // One snapshot of the root rows, read once: the verdict is judged over them\n // and a covered answer is joined from them, so a pull between the judgment\n // and the answer cannot add a root row whose edge windows are not pulled\n // (#426). A failed read of the root is uncovered, as a failed read of the\n // rows is.\n const queryOnce = (\n read: QueryModule.Read\n ): Effect.Effect<ReadonlyArray<ResolvedRow>, ClientView.ClientViewError> => {\n const readThrough = view.read;\n if (readThrough === undefined) return Query.run(view, nodes, read);\n const compiled = Query.plan(nodes, read);\n const local = Effect.suspend(() => Query.run(view, nodes, read));\n return view.query(compiled.root).pipe(\n Effect.orElseSucceed(() => undefined),\n Effect.flatMap((snapshot) =>\n snapshot === undefined\n ? Query.runPlanVia(readThrough, nodes, compiled).pipe(Effect.catch(() => local))\n : verdictOf(\n compiled,\n Coverage.snapshotOf({ subset: compiled.root, rows: snapshot }, view.query)\n ).pipe(\n Effect.flatMap((verdict) =>\n verdict._tag === \"Covered\"\n ? Query.runRows(view, nodes, read, snapshot)\n : Query.runPlanVia(readThrough, nodes, compiled).pipe(Effect.catch(() => local))\n )\n )\n )\n );\n };\n\n const observe = (read: QueryModule.Read): Stream.Stream<ReadonlyArray<ResolvedRow>> => {\n const compiled = Query.plan(nodes, read);\n if (Arr.isReadonlyArrayEmpty(compiled.edges)) {\n const registration = Coverage.ofPlan(compiled);\n return Stream.unwrap(\n Effect.acquireRelease(acquire(registration, \"observer\"), () =>\n release(registration, \"observer\")\n ).pipe(\n Effect.as(\n view.observe(compiled.root).pipe(\n Stream.orDie,\n Stream.map((rows) => rows.map((row) => ({ ...row, related: {} })))\n )\n )\n )\n );\n }\n // The view's relational read auto-subscribes root + narrowed foreign\n // windows itself (host-side on a connected engine), scoped to the stream.\n return Stream.unwrap(\n view\n .related(nodes.collections, read.collection, compiled.root, Query.edgeSelects(compiled))\n .pipe(\n Effect.map((stream) =>\n stream.pipe(Stream.map((rows) => Query.applyWindows(compiled, rows)))\n )\n )\n );\n };\n\n return {\n nodes,\n query: queryOnce,\n observe,\n subscribe: (input) =>\n targetOf(input).pipe(\n Effect.flatMap(({ registration, url }) => acquire(registration, \"explicit\", url))\n ),\n unsubscribe: (input) =>\n targetOf(input).pipe(Effect.flatMap(({ registration }) => release(registration, \"explicit\"))),\n subscriptions: Ref.get(ledger).pipe(\n Effect.map((interests) =>\n [...interests.values()].flatMap((entry) =>\n entry.pins > 0 && entry.url !== undefined ? [entry.url] : []\n )\n )\n ),\n coverageOf: (read, options) => {\n const plan = Query.plan(nodes, read);\n return verdictOf(\n plan,\n options?.snapshot === undefined\n ? view.query\n : Coverage.snapshotOf(options.snapshot, view.query)\n );\n },\n reset,\n identity: boundary === undefined ? noBoundary() : boundary.current,\n quarantines: boundary === undefined ? noBoundary() : boundary.quarantines,\n quarantinedMutations: (identity) =>\n boundary === undefined ? noBoundary() : boundary.quarantinedMutations(identity),\n discardQuarantine: (identity) =>\n boundary === undefined ? noBoundary() : boundary.discardQuarantine(identity),\n mutate: (mutation, args) => view.mutate(mutation, args),\n mutateWithAck: (mutation, args) => view.mutateWithAck(mutation, args),\n beginFormMutation: (form, mutation, args) =>\n view.beginFormMutation === undefined\n ? Effect.fail(\n new TransportError({\n message:\n \"Client.beginFormMutation: this view carries no enhanced form submit (it has neither an engine nor a host to ask)\",\n })\n )\n : view.beginFormMutation(form, mutation, args),\n mutationStates: view.mutationStates,\n view,\n };\n};\n\n/**\n * The Client as a service — for runtimes and bindings that carry \"the one\n * per-thread object\" in context (ADR-0022).\n */\nexport const Client = Context.Service<Client>(\"@thomasfosterau/legation/Client\");\n\n/** Options for a Client that owns its engine ({@link make}). */\nexport interface MakeOptions extends ViewOptions {\n readonly nodes: Node.NodeRegistry;\n readonly mutations: Mutator.MutatorRegistry;\n /**\n * The sync-protocol client identity. Defaults to a random id — fine with\n * the default in-memory store; give a stable id alongside a persistent\n * store so the journal survives restarts.\n */\n readonly clientId?: string;\n /**\n * The client group (the server's CVR unit). Defaults to a random id —\n * same caveat as {@link MakeOptions.clientId}.\n */\n readonly clientGroupId?: string;\n /**\n * Row persistence. Resolution order: this option → a `ClientStore` in\n * context (layer-composed apps) → in-memory (the default; pair\n * `@thomasfosterau/legation-browser`'s IndexedDB store for durability).\n */\n readonly store?: ClientStore.ClientStore;\n /**\n * The server connection. `{ url }` builds the standard remote transport\n * (sync RPCs over WebSocket with HTTP fallback — `RpcTransport.\n * fromOrigin`); a full `ClientTransport` plugs in any carrier.\n * Resolution order: this option → a `ClientTransport` in context →\n * **offline** (no server: reads and optimistic writes work locally, the\n * journal retains every intent, and the sync loop is not started).\n */\n readonly transport?: Transport.ClientTransport | { readonly url: string };\n readonly queries?: QueryModule.QueryRegistry;\n /**\n * Sync-loop scheduling: push-on-mutate debounce, interval pull (see\n * {@link ClientEngine.SyncLoopOptions}). Both legs are opt-in — unset,\n * `runSync` still starts (see {@link MakeOptions.sync}) but a journaled\n * mutation is not pushed until a reconnect, a poke-driven pull, or an\n * explicit `push`.\n */\n readonly syncLoop?: ClientEngine.SyncLoopOptions;\n /**\n * Auto-discover indexable fields from registered subsets and index them\n * (issue #113), in addition to any `indexes` a Node/Collection declares.\n * Defaults to `false`: zero-config behaviour is identical to no indexing.\n */\n readonly autoIndex?: boolean;\n /**\n * Start the sync loop automatically. Defaults to `true` with a\n * transport, `false` when the Client defaulted to offline. This alone\n * does not auto-push journaled mutations — see {@link MakeOptions.syncLoop}.\n */\n readonly sync?: boolean;\n /**\n * The identity this replica belongs to (#212). Supplying it turns the\n * identity boundary on: the Client adopts the fingerprint at construction\n * — before the sync loop starts — exactly as {@link Client.reset} would, so\n * a replica left by a different principal is quarantined-and-cleared\n * before a single query can answer from it.\n *\n * This is the one place an app needs to name the fingerprint: setting it\n * here covers a store configured without one. Setting it in both places is\n * harmless (the second adoption is `Unchanged`). Unset leaves the boundary\n * off — today's behaviour.\n */\n readonly identity?: Identity.Fingerprint;\n}\n\n/** The view options of a larger option set, absent keys left absent. */\nconst viewOptions = (options: ViewOptions): ViewOptions =>\n options.urls === undefined ? {} : { urls: options.urls };\n\n/** The `indexes` declared on each of `nodes`' collections, by collection name. */\nconst declaredIndexes = (\n nodes: Node.NodeRegistry\n): Readonly<Record<string, ReadonlyArray<string>>> => {\n const indexes: Record<string, ReadonlyArray<string>> = {};\n Arr.forEach(nodes.collections, ([name, collection]) => {\n if (collection.indexes !== undefined && Arr.isReadonlyArrayNonEmpty(collection.indexes)) {\n indexes[name] = collection.indexes;\n }\n });\n return indexes;\n};\n\n/**\n * What {@link make} built (#142): the Client, and the engine and store it owns\n * in this thread. An app reads and writes through `client`; `engine` and\n * `store` are there for the engine-level capabilities a Client does not\n * expose, such as attaching devtools (`Devtools.make(engine, store, …)`). A\n * Client joined to an engine hosted elsewhere ({@link connect}) owns neither,\n * so it has no such result.\n */\nexport interface Made {\n /** The Client: the app's one object for reads and writes. */\n readonly client: Client;\n /** The engine the Client owns, its sync loop already running when `sync` is on. */\n readonly engine: ClientEngine.ClientEngine;\n /** The store the engine runs over: the one passed, the one in context, or the in-memory default. */\n readonly store: ClientStore.ClientStore;\n}\n\n/**\n * A Client that owns the engine in this thread. Zero wiring works:\n * `Client.make({ nodes, mutations })` is a purely local Client over an\n * in-memory store. Add `transport: { url }` for the standard remote setup,\n * or pass/provide full `ClientStore`/`ClientTransport` implementations for\n * anything else — options win over context, context over defaults.\n *\n * Returns the Client with the engine and store it built ({@link Made}), so\n * an app that needs an engine-level capability (devtools) need not build the\n * engine by hand.\n */\nexport const make = (options: MakeOptions): Effect.Effect<Made, never, Scope.Scope> =>\n Effect.gen(function* () {\n const store =\n options.store ??\n Option.getOrUndefined(yield* Effect.serviceOption(ClientStore.ClientStore)) ??\n (yield* ClientStore.makeMemory);\n\n const clientGroupId = options.clientGroupId ?? `group-${crypto.randomUUID()}`;\n const configured =\n options.transport !== undefined && \"url\" in options.transport\n ? yield* RpcTransport.fromOrigin({ origin: options.transport.url, clientGroupId })\n : options.transport;\n const contextual = Option.getOrUndefined(\n yield* Effect.serviceOption(Transport.ClientTransport)\n );\n const transport = configured ?? contextual ?? Transport.offline;\n // Defaulted offline: nothing to sync with, so the loop stays off\n // unless the caller insists.\n const sync = options.sync ?? (configured ?? contextual) !== undefined;\n\n const indexes = declaredIndexes(options.nodes);\n const engine = yield* ClientEngine.make({\n clientId: options.clientId ?? `client-${crypto.randomUUID()}`,\n clientGroupId,\n mutators: options.mutations,\n ...(options.queries === undefined ? {} : { queries: options.queries }),\n ...(options.syncLoop === undefined ? {} : { syncLoop: options.syncLoop }),\n ...(!R.isEmptyReadonlyRecord(indexes) ? { indexes } : {}),\n ...(options.autoIndex === undefined ? {} : { autoIndex: options.autoIndex }),\n }).pipe(\n Effect.provideService(ClientStore.ClientStore, store),\n Effect.provideService(Transport.ClientTransport, transport)\n );\n // Adopt the identity before the sync loop starts: a mismatched replica\n // must be quarantined and cleared before anything can read from it or\n // push out of it. A store error here is fatal to construction on purpose\n // — a Client that reported an identity it never actually scoped its\n // replica to is the bug this whole boundary exists to prevent.\n if (options.identity !== undefined) yield* engine.reset(options.identity).pipe(Effect.orDie);\n if (sync) yield* Effect.forkScoped(engine.runSync.pipe(Effect.orDie));\n return {\n client: fromView(options.nodes, ClientView.fromEngine(engine), viewOptions(options)),\n engine,\n store,\n };\n });\n\n/** Options for a Client connected to an engine hosted elsewhere. */\nexport interface ConnectOptions extends ViewOptions {\n readonly nodes: Node.NodeRegistry;\n readonly mutations: Mutator.MutatorRegistry;\n readonly channel: MirrorChannel;\n}\n\n/**\n * A Client over an engine hosted elsewhere (service worker, Electron main,\n * a daemon), joined over a `MirrorChannel`. Reads and writes cross the\n * channel; relational reads resolve host-side.\n */\nexport const connect = (options: ConnectOptions): Effect.Effect<Client, never, Scope.Scope> =>\n Effect.gen(function* () {\n const mirror = yield* Mirror.make({ channel: options.channel, mutators: options.mutations });\n return fromView(options.nodes, mirror, viewOptions(options));\n });\n","/**\n * Hydration\n *\n * Move a client's durable state across a serialisation boundary: `dehydrate`\n * reads a {@link ClientStore} into a plain {@link Snapshot}; `hydrate` writes a\n * snapshot back into a store. Between the two the snapshot is JSON — the value\n * an SSR response embeds, a devtools export writes to disk, or a client hands to a\n * worker.\n *\n * ## SSR flow\n *\n * The server renders a page over a *real* engine per request: an in-memory\n * {@link ClientStore} plus a {@link ClientTransport} to the sync server. It\n * `subscribe`s the subsets the page needs, `pull`s once, then `dehydrate`s the\n * store — capturing the confirmed rows, the pull cookie, and any journalled\n * mutations. That snapshot ships to the browser (JSON-encoded in the document).\n * The client builds its durable store, `hydrate`s the snapshot into it, and\n * constructs the engine over that store. The first render already has rows (no\n * loading flash), and because the cookie came across too the client's first\n * pull continues the CVR conversation from where the server left off — no\n * refetch of state the server already sent.\n *\n * The transport is the seam that makes this work off-process. Pull is the\n * correctness path, so subscribe → pull → dehydrate behaves identically\n * against a remote Server: the renderer need not hold the backend. A\n * single-process app passes nothing and gets a loop-back transport over the\n * in-context {@link ServerHandler}; a split deployment (a SvelteKit Worker\n * rendering against a separate API Worker) passes its own carrier.\n *\n * ## Seeded-versions caveat\n *\n * A snapshot carries each row's authoritative `version`. Those versions must be\n * genuine backend versions for CVR diffing to converge cheaply on the next\n * pull: the server compares them against its record and sends only the delta.\n * Self-invented versions are *safe* — they never corrupt state — but they will\n * not match the server's CVR, so the next pull degrades to the reset path (the\n * server sends a `Clear` followed by full puts). The same caveat applies to\n * {@link ClientEngine.seed}.\n *\n * ## Validation\n *\n * `hydrate` decodes the snapshot through {@link Snapshot} before writing,\n * so a `dehydrate → JSON.stringify → JSON.parse → hydrate` round-trip is\n * schema-checked end to end; a malformed snapshot fails as a `StoreError`.\n * Only the *envelopes* are validated — row values and mutation args stay\n * `Unknown` and round-trip untouched (ADR-0014), exactly like the store's own\n * persisted codecs. Use {@link SnapshotFromJson} to fold the JSON step into the\n * decode.\n */\n\nimport * as Arr from \"effect/Array\";\nimport * as Effect from \"effect/Effect\";\nimport * as Option from \"effect/Option\";\nimport * as R from \"effect/Record\";\nimport * as Schema from \"effect/Schema\";\nimport * as ClientEngineModule from \"./ClientEngine.js\";\nimport {\n ClientStore,\n freezePending,\n // oxlint-disable-next-line effect/no-service-constructor-imports -- the SSR render deliberately builds a private, request-scoped in-memory store (`snapshot` needs nothing from context); providing it via a Layer would change the public `R` or share the memoized store across requests.\n makeMemory,\n type ReplicaState,\n StoredRow,\n} from \"./ClientStore.js\";\nimport type { MutatorError, TransportError, VersionNotSupportedError } from \"./Errors.js\";\nimport { StoreError } from \"./Errors.js\";\nimport * as Mutator from \"./Mutator.js\";\nimport type * as Node from \"./Node.js\";\nimport { Mutation } from \"./Protocol.js\";\nimport * as Query from \"./Query.js\";\nimport * as Subset from \"./Subset.js\";\n// oxlint-disable-next-line effect/no-service-constructor-imports -- the in-process SSR path builds a private loop-back transport over the `ServerHandler` in context, per call; a Layer would change `snapshot`'s public requirements.\nimport { ClientTransport, makeInMemory, ServerHandler } from \"./Transport.js\";\n\n/**\n * One dehydrated row: a {@link StoredRow} tagged with the collection it belongs\n * to, so the flat `rows` array reconstructs the per-collection layout on\n * hydrate. `value` stays `Unknown` (envelope-validated only, ADR-0014).\n */\nexport const SnapshotRow = Schema.Struct({\n collection: Schema.String,\n ...StoredRow.fields,\n});\n\n/**\n * A serialisable capture of a {@link ClientStore}: confirmed rows (flattened\n * with their collection), the pull cookie, the pending-mutation journal, the\n * acknowledged-but-unpulled tail (#336), and each client's mutation id counter\n * (#271). The tail and the counters are optional so a snapshot taken before\n * they existed still loads, as an empty tail and no counters. The schema is\n * the source of truth; {@link Snapshot} is derived from it.\n *\n * The counters are what let a client id be reused across the hydration\n * boundary: once acknowledgements have drained the journal, they are the only\n * record of the ids the backend has already applied, and a hydrated store\n * without them would issue those ids again, which the backend skips as\n * duplicates (a silent lost write).\n */\nexport const Snapshot = Schema.Struct({\n rows: Schema.Array(SnapshotRow),\n cookie: Schema.optional(Schema.String),\n journal: Schema.Array(Mutation),\n unpulled: Schema.optional(Schema.Array(Mutation)),\n counters: Schema.optional(\n Schema.Record(\n Schema.String,\n Schema.Number.pipe(Schema.check(Schema.isInt(), Schema.isGreaterThanOrEqualTo(0)))\n )\n ),\n});\n\n/** The type {@link Snapshot} decodes to. */\nexport type Snapshot = typeof Snapshot.Type;\n\n/** The type {@link SnapshotRow} decodes to. */\nexport type SnapshotRow = typeof SnapshotRow.Type;\n\n/**\n * JSON codec for a {@link Snapshot}: `decode` parses a JSON string and validates\n * it in one step (SSR/devtools ingest), `encode` produces the JSON string.\n */\nexport const SnapshotFromJson = Schema.fromJsonString(Snapshot);\n\n/** The type {@link SnapshotFromJson} decodes to. */\nexport type SnapshotFromJson = typeof SnapshotFromJson.Type;\n\nconst storeError = (message: string) => (cause: unknown) => new StoreError({ message, cause });\n\n/**\n * The replica read member by member: the path for a store without `snapshot`. Its reads are not one\n * state, so a pull or a push acknowledgement can land between them (#392).\n */\nconst readMembers = (store: ClientStore): Effect.Effect<ReplicaState, StoreError> =>\n Effect.gen(function* () {\n const rows: Array<{ readonly collection: string; readonly row: StoredRow }> = [];\n yield* Effect.forEach(\n yield* store.listCollections,\n (collection) =>\n Effect.gen(function* () {\n Arr.forEach(yield* store.listRows(collection), (row) => {\n rows.push({ collection, row });\n });\n }),\n { concurrency: 1, discard: true }\n );\n return {\n rows,\n cookie: yield* store.getCookie,\n journal: yield* store.pendingMutations,\n unpulled: yield* store.unpulled,\n counters: yield* store.mutationCounters,\n };\n });\n\n/**\n * Read a {@link ClientStore} into a {@link Snapshot}: enumerate the collections,\n * gather their confirmed rows (flattened with the collection), the cookie, the\n * journal, the unpulled tail and the mutation counters. The tail and the\n * counters are each left out when empty, so a snapshot without them keeps its\n * previous shape.\n *\n * The five parts are one state when the store offers {@link ClientStore.snapshot}\n * (the memory and key-value stores do): a pull or a push acknowledgement lands wholly\n * before or wholly after the read (#392). A store without it is read member by member.\n */\nexport const dehydrate = (store: ClientStore): Effect.Effect<Snapshot, StoreError> =>\n Effect.gen(function* () {\n const state = yield* store.snapshot ?? readMembers(store);\n const base: Snapshot = {\n rows: state.rows.map(({ collection, row }) => ({\n collection,\n id: row.id,\n version: row.version,\n value: row.value,\n })),\n ...(state.cookie !== undefined ? { cookie: state.cookie } : {}),\n journal: state.journal,\n ...(Arr.isReadonlyArrayNonEmpty(state.unpulled) ? { unpulled: state.unpulled } : {}),\n };\n return R.isEmptyRecord(state.counters) ? base : { ...base, counters: state.counters };\n });\n\n/**\n * Write a {@link Snapshot} into a {@link ClientStore}: validate it through\n * {@link Snapshot} (so a malformed snapshot fails as a `StoreError`), then\n * put the rows, set the cookie if present, park the unpulled tail, raise the\n * store's mutation counters to the snapshot's, and append the journal, skipping\n * any entry the tail already holds. A snapshot without a tail parks nothing. An\n * outstanding pull release is finished first.\n *\n * The counters are raised, never lowered (#271): a client id reused after\n * hydration continues above every id the snapshot's store had issued, so it\n * never reissues an id the backend has already applied. A snapshot without\n * counters leaves the store's as they are.\n *\n * A running {@link ClientEngine} built over the store re-reads its view and built\n * indexes afterwards: a direct write does not go through the engine, so without this\n * an indexed query could return a row whose indexed field changed (#350). The re-read\n * runs when a write fails too, since the rows written before the failure are in the\n * store. It reaches only the engines built over this exact store object: a store\n * wrapped in a new object (`ClientStore.of({ ...store })`) is not seen, so its engine\n * can stay stale. The same re-read tracks the appended journal entries in its\n * `mutationStates` as `\"pending\"` (#358), so they settle like a local mutation. Journal ids\n * should be ids this client has not already settled: an id the map has evicted is tracked again,\n * and its outcome event can then follow a second time.\n *\n * The writes are not atomic. Between the first `putRow` and the re-read, an indexed\n * read on an async store can see the old index against the new rows. Once `hydrate`\n * returns or fails, the engine's view and indexes match the store again.\n *\n * `hydrate` does not take the overlay lock of an engine built over the store, so it must\n * not run while that engine is pulling. Its first step finishes an outstanding pull release\n * (#387). If that release is a live pull's, the pull's remaining row writes then land over\n * the snapshot's rows. Hydrate before the engine starts, or while it is stopped.\n */\nexport const hydrate = (store: ClientStore, snapshot: Snapshot): Effect.Effect<void, StoreError> =>\n Effect.gen(function* () {\n const decoded = yield* Schema.decodeUnknownEffect(Snapshot)(snapshot).pipe(\n Effect.mapError(storeError(\"Invalid snapshot\"))\n );\n yield* Effect.gen(function* () {\n // An outstanding pull release goes first: its record would re-apply its patch over the\n // snapshot's rows on the next open, so it is finished before the snapshot is written (#387).\n yield* store.finishRelease;\n yield* Effect.forEach(\n decoded.rows,\n ({ collection, ...row }) =>\n Effect.gen(function* () {\n yield* store.putRow(collection, row);\n }),\n { concurrency: 1, discard: true }\n );\n if (decoded.cookie !== undefined) yield* store.setCookie(decoded.cookie);\n // The tail's effects are already in the rows, and the next pull releases it (#336).\n if (decoded.unpulled !== undefined) yield* store.parkUnpulled(decoded.unpulled);\n // Once acks drain the journal, the counters are the only record of the ids already applied (#271).\n if (decoded.counters !== undefined) yield* store.raiseMutationCounters(decoded.counters);\n // A snapshot read member by member (a store without `snapshot`) can hold a push acknowledgement's\n // entry in both its journal and its tail, since the two reads are not one state. The tail copy wins:\n // it is not journaled again (#387). The store's tail is read too, since an entry the store already\n // parked can sit in a snapshot's journal; `appendMutation` refuses such an entry, so it must be skipped.\n const parked = new Set(\n (yield* store.unpulled).map((m) => `${m.clientId}\\u0000${m.mutationId}`)\n );\n yield* Effect.forEach(\n decoded.journal,\n (mutation) =>\n Effect.gen(function* () {\n if (parked.has(`${mutation.clientId}\\u0000${mutation.mutationId}`)) return;\n yield* store.appendMutation(mutation);\n }),\n { concurrency: 1, discard: true }\n );\n // These entries came from another context: whether they were pushed there\n // is unknowable here, so paced compaction must not renumber them (#249).\n yield* freezePending(store);\n }).pipe(\n // A refused write leaves the rows before it in the store, so the re-read\n // runs on failure too (#350).\n Effect.ensuring(ClientEngineModule.resyncAttachedEngines(store))\n );\n });\n\n/**\n * Whether a store holds nothing: no rows, no cookie, no journalled or unpulled mutations.\n * The \"fresh browser\" test {@link hydrateIfEmpty} gates on.\n */\nexport const isEmpty = (store: ClientStore): Effect.Effect<boolean, StoreError> =>\n Effect.gen(function* () {\n if ((yield* store.getCookie) !== undefined) return false;\n if (Arr.isReadonlyArrayNonEmpty(yield* store.pendingMutations)) return false;\n if (Arr.isReadonlyArrayNonEmpty(yield* store.unpulled)) return false;\n const holding = yield* Effect.findFirst(yield* store.listCollections, (collection) =>\n Effect.map(store.listRows(collection), Arr.isReadonlyArrayNonEmpty)\n );\n return Option.isNone(holding);\n });\n\n/**\n * {@link hydrate}, but only into an empty store; returns whether it hydrated.\n *\n * This is the right client-boot default over a *durable* store: on a repeat\n * visit the store already holds rows, a cookie, and possibly journalled\n * offline mutations that are all fresher than the SSR snapshot — hydrating\n * over them would regress the cookie (and with it the CVR conversation) and\n * clobber newer confirmed rows. An empty store (first visit, cleared storage)\n * takes the snapshot wholesale.\n */\nexport const hydrateIfEmpty = (\n store: ClientStore,\n snapshot: Snapshot\n): Effect.Effect<boolean, StoreError> =>\n Effect.gen(function* () {\n if (!(yield* isEmpty(store))) return false;\n yield* hydrate(store, snapshot);\n return true;\n });\n\n/**\n * Project a snapshot onto a read, locally and synchronously: the snapshot's\n * rows for the read's collection, filtered/sorted/windowed by the read's\n * compiled root window. This is how a component derives its initial value\n * (e.g. the rune hooks' `initial` option) from the page's snapshot — during\n * SSR and on first client render — without touching an engine.\n *\n * `include`d edges are not resolved here: a snapshot is flat rows, and\n * resolving edges is the Client's job once the engine is up. Seed the base\n * rows and let the first live emission fill the edges in.\n */\nexport const rowsFor = (\n snapshot: Snapshot,\n nodes: Node.NodeRegistry,\n read: Query.Read\n): ReadonlyArray<ClientEngineModule.LocalRow> =>\n Subset.apply(\n snapshot.rows\n .filter((row) => row.collection === read.collection)\n .map((row) => ({ id: row.id, value: row.value })),\n Query.plan(nodes, read).root\n );\n\n/** Options for {@link snapshot}. */\nexport interface SnapshotOptions {\n /**\n * The client group the render belongs to — the browser's group id (e.g.\n * carried in an HTTP cookie), so the cookie in the produced snapshot\n * continues that group's CVR conversation on the client's first pull.\n */\n readonly clientGroupId: string;\n /** Client id for the request-scoped engine (never pushes). Default `\"ssr\"`. */\n readonly clientId?: string;\n /** The node graph the reads compile against. */\n readonly nodes: Node.NodeRegistry;\n /** The reads the page needs — subscribed and pulled once. */\n readonly reads: ReadonlyArray<Query.Read>;\n /** Mutation registry for the request-scoped engine. Default: none. */\n readonly mutations?: Mutator.MutatorRegistry;\n /**\n * The carrier the request-scoped engine pulls over. Omit it for the\n * in-process default — a loop-back transport to the in-context\n * {@link ServerHandler} — which is what a single-process app wants.\n *\n * Pass one when the renderer is *not* the process holding the backend: a\n * SvelteKit Worker rendering against a separate API Worker, an SSR tier in\n * front of a remote sync server. Whatever the carrier, it must preserve the\n * CVR conversation end to end — the request's `clientGroupId` reaches the\n * Server unchanged, and the Server's fresh cookie comes back in the pull\n * response — or the snapshot's cookie belongs to a conversation the client\n * is not in, and its first pull silently degrades to the reset path.\n */\n readonly transport?: ClientTransport;\n}\n\n/** {@link SnapshotOptions} with the carrier supplied — no `ServerHandler` needed. */\nexport interface RemoteSnapshotOptions extends SnapshotOptions {\n readonly transport: ClientTransport;\n}\n\n/** What a snapshot render can fail with. */\nexport type SnapshotError = StoreError | TransportError | MutatorError | VersionNotSupportedError;\n\n/** The snapshot render itself, over an already-built carrier. */\nconst renderOver = (\n transport: ClientTransport,\n options: SnapshotOptions\n): Effect.Effect<Snapshot, SnapshotError> =>\n Effect.scoped(\n Effect.gen(function* () {\n const store = yield* makeMemory;\n const engine = yield* ClientEngineModule.make({\n clientId: options.clientId ?? \"ssr\",\n clientGroupId: options.clientGroupId,\n mutators: options.mutations ?? Mutator.registry([]),\n }).pipe(\n Effect.provideService(ClientStore, store),\n Effect.provideService(ClientTransport, transport)\n );\n yield* Effect.forEach(\n options.reads,\n (read) =>\n Effect.gen(function* () {\n yield* engine.subscribe(Query.plan(options.nodes, read).root);\n }),\n { concurrency: 1, discard: true }\n );\n yield* engine.pull;\n return yield* dehydrate(store);\n })\n );\n\n/**\n * Produce an SSR snapshot over a request-scoped engine: build an in-memory\n * {@link ClientStore}, subscribe the page's subsets over the given\n * {@link ClientTransport}, pull once, and {@link dehydrate}. Everything is\n * scoped to the call — nothing outlives it.\n *\n * With `options.transport` the render needs nothing from context; without it\n * the in-process path applies and the app's sync server is taken from context\n * as `ServerHandler`, carried by a loop-back transport.\n *\n * This is the server half of the SSR flow described above; run it inside a\n * request handler (a SvelteKit `load`, a Next.js loader). Framework packages\n * wrap this — they add the framework's own plumbing (a cookie, a `load`\n * signature), never their own hydration logic.\n */\nexport function snapshot(options: RemoteSnapshotOptions): Effect.Effect<Snapshot, SnapshotError>;\n/** The in-process form: renders against the app's sync server, the `ServerHandler` in context. */\nexport function snapshot(\n options: SnapshotOptions\n): Effect.Effect<Snapshot, SnapshotError, ServerHandler>;\nexport function snapshot(\n options: SnapshotOptions\n): Effect.Effect<Snapshot, SnapshotError, ServerHandler> {\n const supplied = options.transport;\n if (supplied !== undefined) return renderOver(supplied, options);\n return Effect.scoped(\n Effect.gen(function* () {\n const handler = yield* ServerHandler;\n const transport = yield* makeInMemory(handler, options.clientGroupId);\n return yield* renderOver(transport, options);\n })\n );\n}\n","/**\n * Server\n *\n * The one server-side object (ADR-0022): built once over the {@link Backend}\n * and auth seams, it exposes the protocol **handler** (push/pull with CVR\n * diffing — mount it on any router or carrier) and **broadcast**, the single\n * verb for \"these resources changed\".\n *\n * Mutation writes pushed through the handler broadcast implicitly (the push\n * path publishes their hints); `broadcast` is for everything else — cron\n * jobs, admin tools, any writer that bypasses mutations (ADR-0019's\n * out-of-band story in one line). Underneath it is unchanged poke-then-pull:\n * hints are droppable latency optimizations, pull is the correctness path.\n *\n * CVR storage and the fan-out bus are **options with working defaults**\n * (in-memory store, in-process bus); scale-out swaps them (a shared\n * KeyValueStore, a serialized broker — ADR-0011) without touching app code.\n * The Server is logical: its parts deploy separately (per-user rooms,\n * fan-out shards — ADR-0022's reference topology), and ADR-0023's\n * topic-derived fan-out lands behind `broadcast` without changing its\n * signature.\n *\n * A third verb, **mutate**, runs one named Mutation server-side as one of a\n * client group's mutations (ADR-0030, amendment #215): the entry point a form\n * action calls. It is a push minus the transport, so it goes through the\n * handler's push path (identity, middleware, the backend's ledger, the\n * broadcast), and it answers a {@link Receipt} carrying the backend's ruling.\n */\n\nimport * as Clock from \"effect/Clock\";\nimport * as Effect from \"effect/Effect\";\nimport * as Schema from \"effect/Schema\";\nimport * as Scope from \"effect/Scope\";\nimport * as Authorization from \"./Authorization.js\";\nimport { Backend, type Backend as BackendService } from \"./Backend.js\";\nimport * as Cvr from \"./Cvr.js\";\nimport { BackendError, type TransportError, type VersionNotSupportedError } from \"./Errors.js\";\nimport * as InvalidationBus from \"./InvalidationBus.js\";\nimport type * as Mutator from \"./Mutator.js\";\nimport type { PushResponse } from \"./Protocol.js\";\nimport * as Query from \"./Query.js\";\nimport * as SyncServer from \"./SyncServer.js\";\nimport type * as Transport from \"./Transport.js\";\n\n/** A change announcement for {@link Server.broadcast}. */\nexport interface Broadcast {\n readonly collection: string;\n /** The changed row ids, when the writer knows them (finer fan-out). */\n readonly ids?: ReadonlyArray<string>;\n}\n\n/**\n * One Mutation for {@link Server.mutate} to run: the client group, the client\n * and mutation id that name it, and the Mutation with its args.\n *\n * A form action passes a **form client** id minted for the rendered form, with\n * mutation id `1` (ADR-0030 #215 §2). A mutation id is per client and\n * contiguous, so the action must not borrow the next id of a tab's engine.\n */\nexport interface MutateRequest<Args, ArgsEncoded> {\n /** The client group the mutation belongs to (ADR-0020's client-group cookie). */\n readonly clientGroupId: string;\n /** The client that names the mutation: a form client id for a form action. */\n readonly clientId: string;\n /** The mutation's id for that client: `1` for a freshly rendered form. */\n readonly mutationId: number;\n /** The Mutation definition to run; its name selects the backend's handler. */\n readonly mutation: Mutator.Mutator<string, Args, ArgsEncoded>;\n /**\n * The args, as the form layer hands them over: decoded and validated. They\n * are encoded against `mutation.args` for the backend, which decodes them\n * again as for a pushed mutation.\n */\n readonly args: Args;\n /** The schema version to check, as a push carries it (#48); unset when omitted. */\n readonly schemaVersion?: string | undefined;\n}\n\n/**\n * What {@link Server.mutate} answers: the mutation it ran, named by its client\n * and mutation id, and the backend's ruling (ADR-0030 #215 §3). The phase uses\n * `MutationState`'s vocabulary: `acknowledged`, or `rejected` with the reason\n * the backend gave. A client holding an optimistic effect for this mutation\n * releases it when a pull's `lastMutationIds` covers it, not on the receipt.\n */\nexport type Receipt =\n | {\n readonly clientId: string;\n readonly mutationId: number;\n readonly phase: \"acknowledged\";\n }\n | {\n readonly clientId: string;\n readonly mutationId: number;\n readonly phase: \"rejected\";\n /** The backend's permanent-rejection reason. */\n readonly reason: string;\n };\n\n/** One per backend: the protocol `handler` plus `broadcast` for out-of-band changes. */\nexport interface Server {\n /** The sync protocol handler — mount it on any router or carrier. */\n readonly handler: Transport.ServerHandler;\n /**\n * Announce out-of-band changes: subscribers whose windows may be\n * affected are poked and pull. Writes pushed through {@link handler}\n * broadcast implicitly.\n */\n readonly broadcast: (change: Broadcast) => Effect.Effect<void>;\n /**\n * Run one named Mutation as one of a client group's mutations, and answer\n * its {@link Receipt} (ADR-0030 #215 §1–§3).\n *\n * The path is the push path minus the transport: the group's identity is\n * resolved through the `IdentityResolver`, the middleware's `onPush` runs,\n * the backend applies the mutation with its id bumped in the same\n * transaction as its effects (a permanent rejection bumps it with no\n * effects, ADR-0008), and the write's hints are broadcast as a pushed\n * write's are. A pull from any client of the group then lists `clientId` in\n * `lastMutationIds`.\n *\n * A resubmission of the same `(clientId, mutationId)` is a replay: the\n * backend's ledger skips it, the handler does not run again, and the call\n * answers the original ruling (a rejection from the backend's rejection\n * memory, Backend guarantee 4).\n *\n * Args that do not encode against the Mutation's schema are a defect of the\n * hand-over from the form layer, not a field error (§5): the effect dies.\n * Every other failure (identity, middleware, a transient backend error, an\n * unsupported schema version) fails as the push path does.\n */\n readonly mutate: <Args, ArgsEncoded>(\n request: MutateRequest<Args, ArgsEncoded>\n ) => Effect.Effect<Receipt, TransportError | VersionNotSupportedError>;\n}\n\n/**\n * Read one mutation's ruling off a push response: rejected when the response\n * lists it, acknowledged when the client's watermark covers it. A response\n * that does neither breaks Backend guarantee 1, so it is a defect.\n */\nconst receiptOf = (\n response: PushResponse,\n clientId: string,\n mutationId: number\n): Effect.Effect<Receipt> => {\n const rejected = response.rejected?.find(\n (entry) => entry.clientId === clientId && entry.mutationId === mutationId\n );\n if (rejected !== undefined) {\n return Effect.succeed({ clientId, mutationId, phase: \"rejected\", reason: rejected.reason });\n }\n const watermark = response.lastMutationIds[clientId] ?? 0;\n return watermark >= mutationId\n ? Effect.succeed({ clientId, mutationId, phase: \"acknowledged\" })\n : Effect.die(\n new BackendError({\n message: `Server.mutate: the backend neither applied nor rejected mutation ${mutationId} of client ${clientId} (its watermark is ${watermark})`,\n })\n );\n};\n\n/**\n * Options for {@link make}: the backend and auth are required; CVR storage and the bus default\n * to in-memory / in-process.\n */\nexport interface MakeOptions {\n /** The storage contract (ADR-0009). */\n readonly backend: BackendService;\n readonly auth: {\n /** Deny-by-default per-collection filters. */\n readonly policy: Authorization.AuthPolicy;\n /** Resolve a client group to its identity (session binding). */\n readonly identity?: Authorization.IdentityResolver;\n };\n /** Named-query resolution (server-authoritative subsets). */\n readonly queries?: Query.QueryResolver;\n /** Cross-cutting interception (tenancy, cost control, telemetry). */\n readonly middleware?: Authorization.SyncMiddleware;\n /** CVR storage; defaults to in-memory. */\n readonly cvr?: Cvr.CvrStore;\n /** The hint fan-out bus; defaults to in-process. */\n readonly bus?: InvalidationBus.InvalidationBus;\n /** Handler tuning passthrough. */\n readonly options?: SyncServer.SyncServerOptions;\n}\n\n/** Build the Server: handler + broadcast over backend and auth. */\nexport const make = (options: MakeOptions): Effect.Effect<Server, never, Scope.Scope> =>\n Effect.gen(function* () {\n const cvr = options.cvr ?? (yield* Cvr.makeMemoryStore);\n const bus = options.bus ?? (yield* InvalidationBus.makeInProcess);\n let handler = SyncServer.makeWith(options.options).pipe(\n Effect.provideService(Backend, options.backend),\n Effect.provideService(Authorization.AuthPolicy, options.auth.policy),\n Effect.provideService(Cvr.CvrStore, cvr),\n Effect.provideService(InvalidationBus.InvalidationBus, bus)\n );\n if (options.auth.identity !== undefined) {\n handler = handler.pipe(\n Effect.provideService(Authorization.IdentityResolver, options.auth.identity)\n );\n }\n if (options.queries !== undefined) {\n handler = handler.pipe(Effect.provideService(Query.QueryResolver, options.queries));\n }\n if (options.middleware !== undefined) {\n handler = handler.pipe(\n Effect.provideService(Authorization.SyncMiddleware, options.middleware)\n );\n }\n const built = yield* handler;\n const mutate = <Args, ArgsEncoded>(\n request: MutateRequest<Args, ArgsEncoded>\n ): Effect.Effect<Receipt, TransportError | VersionNotSupportedError> =>\n Effect.gen(function* () {\n const args = yield* Schema.encodeEffect(request.mutation.args)(request.args).pipe(\n Effect.orDie\n );\n const createdAt = yield* Clock.currentTimeMillis;\n const response = yield* built.handlePush({\n clientGroupId: request.clientGroupId,\n mutations: [\n {\n clientId: request.clientId,\n mutationId: request.mutationId,\n mutator: request.mutation.name,\n args,\n createdAt,\n },\n ],\n schemaVersion: request.schemaVersion,\n });\n return yield* receiptOf(response, request.clientId, request.mutationId);\n });\n return {\n handler: built,\n broadcast: (change) =>\n bus.publish([{ collection: change.collection, ids: change.ids ?? [] }]),\n mutate,\n };\n });\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,72 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@thomasfosterau/legation",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "An offline-first sync engine over a typed resource graph: Node (with its Edges), Collection, declarative Query reads, named Mutation writes, one Client per thread and one Server per backend",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"cache",
|
|
7
|
+
"edge",
|
|
8
|
+
"effect",
|
|
9
|
+
"fan-out",
|
|
10
|
+
"mutation",
|
|
11
|
+
"offline-first",
|
|
12
|
+
"postgres",
|
|
13
|
+
"query",
|
|
14
|
+
"service-worker",
|
|
15
|
+
"sql",
|
|
16
|
+
"sqlite",
|
|
17
|
+
"subscription",
|
|
18
|
+
"sync-engine",
|
|
19
|
+
"typescript",
|
|
20
|
+
"write-through"
|
|
21
|
+
],
|
|
22
|
+
"homepage": "https://github.com/thomasfosterau/legation/tree/trunk/packages/effect#readme",
|
|
23
|
+
"bugs": {
|
|
24
|
+
"url": "https://github.com/thomasfosterau/legation/issues"
|
|
25
|
+
},
|
|
26
|
+
"license": "MIT",
|
|
27
|
+
"author": "Thomas Foster",
|
|
28
|
+
"repository": {
|
|
29
|
+
"type": "git",
|
|
30
|
+
"url": "git+https://github.com/thomasfosterau/legation.git",
|
|
31
|
+
"directory": "packages/effect"
|
|
32
|
+
},
|
|
33
|
+
"files": [
|
|
34
|
+
"dist",
|
|
35
|
+
"README.md",
|
|
36
|
+
"LICENSE",
|
|
37
|
+
"CHANGELOG.md"
|
|
38
|
+
],
|
|
39
|
+
"type": "module",
|
|
40
|
+
"sideEffects": false,
|
|
41
|
+
"main": "./dist/index.js",
|
|
42
|
+
"types": "./dist/index.d.ts",
|
|
43
|
+
"exports": {
|
|
44
|
+
".": {
|
|
45
|
+
"types": "./dist/index.d.ts",
|
|
46
|
+
"import": "./dist/index.js"
|
|
47
|
+
},
|
|
48
|
+
"./internal": {
|
|
49
|
+
"types": "./dist/internal.d.ts",
|
|
50
|
+
"import": "./dist/internal.js"
|
|
51
|
+
},
|
|
52
|
+
"./sql": {
|
|
53
|
+
"types": "./dist/sql/index.d.ts",
|
|
54
|
+
"import": "./dist/sql/index.js"
|
|
55
|
+
},
|
|
56
|
+
"./package.json": "./package.json"
|
|
57
|
+
},
|
|
58
|
+
"publishConfig": {
|
|
59
|
+
"access": "public"
|
|
60
|
+
},
|
|
61
|
+
"devDependencies": {
|
|
62
|
+
"@effect/platform-node": "4.1.2",
|
|
63
|
+
"@effect/sql-pg": "4.1.2",
|
|
64
|
+
"@effect/sql-sqlite-node": "4.1.2",
|
|
65
|
+
"@effect/vitest": "4.1.2",
|
|
66
|
+
"effect": "4.1.2",
|
|
67
|
+
"vitest": "5.0.5"
|
|
68
|
+
},
|
|
69
|
+
"peerDependencies": {
|
|
70
|
+
"effect": "^4.0.0"
|
|
71
|
+
}
|
|
6
72
|
}
|