@thomasfosterau/pulpit 0.0.0 → 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/LICENSE +21 -0
- package/README.md +1271 -1
- package/dist/blobs.d.ts +249 -0
- package/dist/blobs.d.ts.map +1 -0
- package/dist/blobs.js +193 -0
- package/dist/blobs.js.map +1 -0
- package/dist/budgets.d.ts +255 -0
- package/dist/budgets.d.ts.map +1 -0
- package/dist/budgets.js +211 -0
- package/dist/budgets.js.map +1 -0
- package/dist/cache.d.ts +154 -0
- package/dist/cache.d.ts.map +1 -0
- package/dist/cache.js +118 -0
- package/dist/cache.js.map +1 -0
- package/dist/collections.d.ts +242 -0
- package/dist/collections.d.ts.map +1 -0
- package/dist/collections.js +215 -0
- package/dist/collections.js.map +1 -0
- package/dist/comments.d.ts +212 -0
- package/dist/comments.d.ts.map +1 -0
- package/dist/comments.js +192 -0
- package/dist/comments.js.map +1 -0
- package/dist/content.d.ts +231 -0
- package/dist/content.d.ts.map +1 -0
- package/dist/content.js +204 -0
- package/dist/content.js.map +1 -0
- package/dist/documents.d.ts +184 -0
- package/dist/documents.d.ts.map +1 -0
- package/dist/documents.js +190 -0
- package/dist/documents.js.map +1 -0
- package/dist/edges.d.ts +645 -0
- package/dist/edges.d.ts.map +1 -0
- package/dist/edges.js +558 -0
- package/dist/edges.js.map +1 -0
- package/dist/filter.d.ts +461 -0
- package/dist/filter.d.ts.map +1 -0
- package/dist/filter.js +387 -0
- package/dist/filter.js.map +1 -0
- package/dist/index.d.ts +133 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +133 -0
- package/dist/index.js.map +1 -0
- package/dist/internal/filter-columns.d.ts +8 -0
- package/dist/internal/filter-columns.d.ts.map +1 -0
- package/dist/internal/filter-columns.js +43 -0
- package/dist/internal/filter-columns.js.map +1 -0
- package/dist/internal/list-order.d.ts +26 -0
- package/dist/internal/list-order.d.ts.map +1 -0
- package/dist/internal/list-order.js +92 -0
- package/dist/internal/list-order.js.map +1 -0
- package/dist/internal/owned-columns.d.ts +93 -0
- package/dist/internal/owned-columns.d.ts.map +1 -0
- package/dist/internal/owned-columns.js +98 -0
- package/dist/internal/owned-columns.js.map +1 -0
- package/dist/internal/plain-rows.d.ts +32 -0
- package/dist/internal/plain-rows.d.ts.map +1 -0
- package/dist/internal/plain-rows.js +32 -0
- package/dist/internal/plain-rows.js.map +1 -0
- package/dist/internal/reference-columns.d.ts +80 -0
- package/dist/internal/reference-columns.d.ts.map +1 -0
- package/dist/internal/reference-columns.js +102 -0
- package/dist/internal/reference-columns.js.map +1 -0
- package/dist/moderation.d.ts +392 -0
- package/dist/moderation.d.ts.map +1 -0
- package/dist/moderation.js +375 -0
- package/dist/moderation.js.map +1 -0
- package/dist/paths.d.ts +204 -0
- package/dist/paths.d.ts.map +1 -0
- package/dist/paths.js +165 -0
- package/dist/paths.js.map +1 -0
- package/dist/proposals.d.ts +523 -0
- package/dist/proposals.d.ts.map +1 -0
- package/dist/proposals.js +505 -0
- package/dist/proposals.js.map +1 -0
- package/dist/publication.d.ts +148 -0
- package/dist/publication.d.ts.map +1 -0
- package/dist/publication.js +183 -0
- package/dist/publication.js.map +1 -0
- package/dist/purge.d.ts +224 -0
- package/dist/purge.d.ts.map +1 -0
- package/dist/purge.js +151 -0
- package/dist/purge.js.map +1 -0
- package/dist/reactions.d.ts +246 -0
- package/dist/reactions.d.ts.map +1 -0
- package/dist/reactions.js +183 -0
- package/dist/reactions.js.map +1 -0
- package/dist/registry.d.ts +316 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/registry.js +305 -0
- package/dist/registry.js.map +1 -0
- package/dist/search.d.ts +366 -0
- package/dist/search.d.ts.map +1 -0
- package/dist/search.js +273 -0
- package/dist/search.js.map +1 -0
- package/dist/social.d.ts +311 -0
- package/dist/social.d.ts.map +1 -0
- package/dist/social.js +205 -0
- package/dist/social.js.map +1 -0
- package/dist/subtype.d.ts +1613 -0
- package/dist/subtype.d.ts.map +1 -0
- package/dist/subtype.js +1300 -0
- package/dist/subtype.js.map +1 -0
- package/dist/testing.d.ts +675 -0
- package/dist/testing.d.ts.map +1 -0
- package/dist/testing.js +1022 -0
- package/dist/testing.js.map +1 -0
- package/dist/version-chain.d.ts +702 -0
- package/dist/version-chain.d.ts.map +1 -0
- package/dist/version-chain.js +489 -0
- package/dist/version-chain.js.map +1 -0
- package/dist/working-copy.d.ts +116 -0
- package/dist/working-copy.d.ts.map +1 -0
- package/dist/working-copy.js +95 -0
- package/dist/working-copy.js.map +1 -0
- package/package.json +138 -4
- package/src/blobs.ts +384 -0
- package/src/budgets.ts +338 -0
- package/src/cache.ts +218 -0
- package/src/collections.ts +427 -0
- package/src/comments.ts +392 -0
- package/src/content.ts +238 -0
- package/src/documents.ts +284 -0
- package/src/edges.ts +1185 -0
- package/src/filter.ts +523 -0
- package/src/index.ts +153 -0
- package/src/internal/filter-columns.ts +50 -0
- package/src/internal/list-order.ts +117 -0
- package/src/internal/owned-columns.ts +140 -0
- package/src/internal/plain-rows.ts +54 -0
- package/src/internal/reference-columns.ts +156 -0
- package/src/moderation.ts +728 -0
- package/src/paths.ts +267 -0
- package/src/proposals.ts +1014 -0
- package/src/publication.ts +225 -0
- package/src/purge.ts +313 -0
- package/src/reactions.ts +347 -0
- package/src/registry.ts +524 -0
- package/src/search.ts +540 -0
- package/src/social.ts +469 -0
- package/src/subtype.ts +3095 -0
- package/src/testing.ts +1873 -0
- package/src/version-chain.ts +1093 -0
- package/src/working-copy.ts +192 -0
package/dist/edges.d.ts
ADDED
|
@@ -0,0 +1,645 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* **The edge operations factory** — a relation between nodes as its own
|
|
3
|
+
* family: a bounded list, a read by id, an idempotent create and a hard
|
|
4
|
+
* delete, gated by the visibility of the nodes the edge joins.
|
|
5
|
+
*
|
|
6
|
+
* The behavioural spec is gardeneria's `apps/api/src/operations/edge-ops.ts`
|
|
7
|
+
* (`defineEdgeOpsE`), which exists because hand-rolled edge families drift,
|
|
8
|
+
* and the ninth family's viewer gate or `LIMIT` gets forgotten. Its
|
|
9
|
+
* hand-rolled owned-child family (`disorders.ts`, the observation → disorder
|
|
10
|
+
* edge) is the spec for the endpoint gates. A `#NNN` here names a gardeneria
|
|
11
|
+
* issue.
|
|
12
|
+
*
|
|
13
|
+
* ## What an edge is
|
|
14
|
+
*
|
|
15
|
+
* An edge is a plain row in its own table: an id, a `createdAt`, one column
|
|
16
|
+
* per **end** holding the id of the node at that end, and any qualifier
|
|
17
|
+
* columns of the family's own. It is **not** a node: it has no version chain,
|
|
18
|
+
* so the chain invariants (`VERSION-CHAIN-001/002`) do not govern it. A delete
|
|
19
|
+
* is a hard `DELETE`, and there is no update: correcting an edge is deleting
|
|
20
|
+
* it and asserting the right one. A family declares zero, one or two (or
|
|
21
|
+
* more) ends; with none, it is a plain bounded row family (gardeneria's
|
|
22
|
+
* `page_collections`).
|
|
23
|
+
*
|
|
24
|
+
* ## The invariants
|
|
25
|
+
*
|
|
26
|
+
* - **An edge is readable only if the viewer can read every end**
|
|
27
|
+
* (`EDGE-001`). Each end's node is checked against its own leaf's access
|
|
28
|
+
* declaration, as that leaf's own `get` and list filter read it (a
|
|
29
|
+
* reference leaf's followers arm included, pulpit#92). `get` answers a
|
|
30
|
+
* hidden edge as {@link EdgeNotFound} and `list` omits it, never `Forbidden`
|
|
31
|
+
* (`SEC-AUTH-ENUM-001`); a related list (the edges at one node) is a `list`
|
|
32
|
+
* filtered on that end, so it omits every edge whose other end is hidden.
|
|
33
|
+
* - **The list filter and the per-edge predicate never disagree.**
|
|
34
|
+
* {@link endpointFilter} renders the same facts {@link canViewEndpoint}
|
|
35
|
+
* reads, over the ends' joined views; `tests/edges.test.ts` checks every
|
|
36
|
+
* endpoint state × viewer.
|
|
37
|
+
* - **A tombstoned end hides its edges** from every viewer below the
|
|
38
|
+
* view-deleted rung. That rung reads a tombstoned end as the leaf's own
|
|
39
|
+
* `get` reads the tombstone, by its access rule (pulpit#93), so it can
|
|
40
|
+
* list, read and delete an edge whose only hidden end is tombstoned; an
|
|
41
|
+
* end hidden for any other reason (a `private` node below the
|
|
42
|
+
* view-private rung, say) still hides the edge from it. Nothing is
|
|
43
|
+
* deleted: restoring the node (the explicit `restore`,
|
|
44
|
+
* `VERSION-CHAIN-006`) brings its edges back as they were.
|
|
45
|
+
* - **A create names only live nodes the viewer can read.** An end naming a
|
|
46
|
+
* missing, hidden or tombstoned node fails `NodeNotFound` (the
|
|
47
|
+
* view-deleted rung included), so a create cannot confirm a node exists.
|
|
48
|
+
* - **Cardinality is declared, and a create past it fails**
|
|
49
|
+
* ({@link EdgeCardinalityExceeded}). An end's `maxEdges` bounds how many
|
|
50
|
+
* edges may share one node at that end, counting only the edges the
|
|
51
|
+
* caller can read whose every end is live (pulpit#77, pulpit#93). A
|
|
52
|
+
* hidden edge never counts, so a refusal discloses nothing the caller
|
|
53
|
+
* can't read; the cost is a limit that holds only over what each caller
|
|
54
|
+
* sees, and a restore that can bring a node past it (see
|
|
55
|
+
* {@link EdgeEnd.maxEdges}). A re-assertion of a stored edge resolves
|
|
56
|
+
* before the count, so it stays idempotent at a full node.
|
|
57
|
+
* - **A write discloses no hidden edge's state.** A create runs in the
|
|
58
|
+
* order ends → write gate → unique-key collision → cardinality count.
|
|
59
|
+
* Under `"fail"` a collision with a hidden edge answers exactly as one
|
|
60
|
+
* with a readable edge; under `"ignore"` only a readable, live edge
|
|
61
|
+
* resolves, and any other collision is {@link EdgeConflict}. The count
|
|
62
|
+
* reads only readable edges (README → `./edges` → What a write
|
|
63
|
+
* discloses).
|
|
64
|
+
* - **Every list is bounded** (`SEC-INPUT-003`): a page is capped at
|
|
65
|
+
* `MAX_PAGE_LIMIT`.
|
|
66
|
+
*
|
|
67
|
+
* ## The seam
|
|
68
|
+
*
|
|
69
|
+
* The edges are read and written through an {@link EdgeStore} the
|
|
70
|
+
* application supplies (a Drizzle store over the edge table, joining each
|
|
71
|
+
* end's current-version view; see the package README → Seams). Each end
|
|
72
|
+
* reads its nodes through an {@link EndpointView}, which a `./subtype`
|
|
73
|
+
* `LeafStore` already satisfies.
|
|
74
|
+
*
|
|
75
|
+
* @copyright Thomas Foster
|
|
76
|
+
* @module pulpit/edges
|
|
77
|
+
* @since 0.1.0
|
|
78
|
+
*/
|
|
79
|
+
import * as Effect from "effect/Effect";
|
|
80
|
+
import * as HashMap from "effect/HashMap";
|
|
81
|
+
import * as Option from "effect/Option";
|
|
82
|
+
import * as Schema from "effect/Schema";
|
|
83
|
+
import { type ListFilter } from "./filter.ts";
|
|
84
|
+
import { Forbidden, Unauthorized, type AccessPolicy, type FollowerSeam, type LeafAccess, type LeafRow, type Page, type Viewer, type ViewerAccount } from "./subtype.ts";
|
|
85
|
+
import { NodeNotFound, type ColumnValue } from "./version-chain.ts";
|
|
86
|
+
declare const EdgeNotFound_base: Schema.Class<EdgeNotFound, Schema.TaggedStruct<"EdgeNotFound", {
|
|
87
|
+
readonly type: Schema.String;
|
|
88
|
+
readonly id: Schema.String;
|
|
89
|
+
}>, import("effect/Cause").YieldableError & {
|
|
90
|
+
readonly brand: unique symbol;
|
|
91
|
+
}>;
|
|
92
|
+
/**
|
|
93
|
+
* No edge with this id that the viewer may read: missing, or hidden because
|
|
94
|
+
* an end is (`SEC-AUTH-ENUM-001`, `EDGE-001`). The wire's 404.
|
|
95
|
+
*
|
|
96
|
+
* @since 0.1.0
|
|
97
|
+
*/
|
|
98
|
+
export declare class EdgeNotFound extends EdgeNotFound_base {
|
|
99
|
+
}
|
|
100
|
+
declare const EdgeConflict_base: Schema.Class<EdgeConflict, Schema.TaggedStruct<"EdgeConflict", {
|
|
101
|
+
readonly type: Schema.String;
|
|
102
|
+
}>, import("effect/Cause").YieldableError & {
|
|
103
|
+
readonly brand: unique symbol;
|
|
104
|
+
}>;
|
|
105
|
+
/**
|
|
106
|
+
* A create collided with a stored edge on the family's unique key, and the
|
|
107
|
+
* family's conflict policy does not resolve it: `onConflict: "fail"`, or the
|
|
108
|
+
* edge it collided with is one the viewer may not read or has a tombstoned
|
|
109
|
+
* end. The wire's 409. It carries only the family's type, so under `"fail"`
|
|
110
|
+
* a collision with a hidden edge is the same answer as one with a readable
|
|
111
|
+
* edge (`SEC-AUTH-ENUM-001`, pulpit#93); under `"ignore"` only a readable,
|
|
112
|
+
* live edge resolves, and any other collision is this error. It says the
|
|
113
|
+
* key is taken, never by which edge.
|
|
114
|
+
*
|
|
115
|
+
* @since 0.1.0
|
|
116
|
+
*/
|
|
117
|
+
export declare class EdgeConflict extends EdgeConflict_base {
|
|
118
|
+
}
|
|
119
|
+
declare const EdgeCardinalityExceeded_base: Schema.Class<EdgeCardinalityExceeded, Schema.TaggedStruct<"EdgeCardinalityExceeded", {
|
|
120
|
+
/** The edge family's wire type. */
|
|
121
|
+
readonly type: Schema.String;
|
|
122
|
+
/** The end (its alias in the declaration) whose limit was reached. */
|
|
123
|
+
readonly end: Schema.String;
|
|
124
|
+
/** The node at that end, which already holds `max` edges the caller can read. */
|
|
125
|
+
readonly nodeId: Schema.String;
|
|
126
|
+
/** The declared limit. */
|
|
127
|
+
readonly max: Schema.Int;
|
|
128
|
+
}>, import("effect/Cause").YieldableError & {
|
|
129
|
+
readonly brand: unique symbol;
|
|
130
|
+
}>;
|
|
131
|
+
/**
|
|
132
|
+
* A create would put more edges at one node than its end's declared
|
|
133
|
+
* `maxEdges` allows, counting only the edges the caller can read whose every
|
|
134
|
+
* end is live ({@link EdgeEnd.maxEdges}). The wire's 409.
|
|
135
|
+
*
|
|
136
|
+
* @since 0.1.0
|
|
137
|
+
*/
|
|
138
|
+
export declare class EdgeCardinalityExceeded extends EdgeCardinalityExceeded_base {
|
|
139
|
+
}
|
|
140
|
+
declare const EdgeDeclarationError_base: Schema.Class<EdgeDeclarationError, Schema.TaggedStruct<"EdgeDeclarationError", {
|
|
141
|
+
readonly message: Schema.String;
|
|
142
|
+
}>, import("effect/Cause").YieldableError & {
|
|
143
|
+
readonly brand: unique symbol;
|
|
144
|
+
}>;
|
|
145
|
+
/**
|
|
146
|
+
* An edge family declared against the rules: an end's alias contains a `.`,
|
|
147
|
+
* or its `maxEdges` is not a positive safe integer. Thrown by
|
|
148
|
+
* {@link defineEdgeOps} when the family is declared, never an Effect failure,
|
|
149
|
+
* like `RegistryDeclarationError`: a malformed family must stop its module
|
|
150
|
+
* loading rather than fail open (a `NaN` limit admits every create) or
|
|
151
|
+
* refuse every create (a limit of `0`).
|
|
152
|
+
*
|
|
153
|
+
* @since 0.1.0
|
|
154
|
+
*/
|
|
155
|
+
export declare class EdgeDeclarationError extends EdgeDeclarationError_base {
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* An end's view of its nodes: the endpoint leaf's current-version view
|
|
159
|
+
* column set and its read by id. A `./subtype` `LeafStore` satisfies it, so
|
|
160
|
+
* an end usually passes the endpoint leaf's own store.
|
|
161
|
+
*
|
|
162
|
+
* @since 0.1.0
|
|
163
|
+
*/
|
|
164
|
+
export interface EndpointView<E, R> {
|
|
165
|
+
/** The endpoint view's column names; an owned declaration naming a column outside it hides every edge. */
|
|
166
|
+
readonly columns: ReadonlySet<string>;
|
|
167
|
+
/** One node of the current-version view by id, or none. Applies no visibility policy. */
|
|
168
|
+
readonly get: (id: string) => Effect.Effect<Option.Option<LeafRow>, E, R>;
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* One end of an edge family: the edge column holding the node id, and how
|
|
172
|
+
* that node is read and gated.
|
|
173
|
+
*
|
|
174
|
+
* @since 0.1.0
|
|
175
|
+
*/
|
|
176
|
+
export interface EdgeEnd<E, R> {
|
|
177
|
+
/** The edge column holding this end's node id. */
|
|
178
|
+
readonly column: string;
|
|
179
|
+
/** The endpoint leaf's wire type: names the `NodeNotFound` a create naming a missing or hidden node fails with. */
|
|
180
|
+
readonly type: string;
|
|
181
|
+
/** The endpoint leaf's access declaration, the one its own `defineSubtype` takes. */
|
|
182
|
+
readonly access: LeafAccess;
|
|
183
|
+
/** The endpoint leaf's current-version view. */
|
|
184
|
+
readonly nodes: EndpointView<E, R>;
|
|
185
|
+
/**
|
|
186
|
+
* The most edges one node at this end may hold: a positive safe integer,
|
|
187
|
+
* or {@link defineEdgeOps} throws {@link EdgeDeclarationError}. Absent,
|
|
188
|
+
* unlimited. A create that would exceed it fails
|
|
189
|
+
* {@link EdgeCardinalityExceeded}.
|
|
190
|
+
*
|
|
191
|
+
* A create counts only the edges at the node that the caller can read
|
|
192
|
+
* (`EDGE-001`) and whose every end is live (owner decisions on pulpit#77
|
|
193
|
+
* and pulpit#93), which has three consequences:
|
|
194
|
+
*
|
|
195
|
+
* - **No disclosure.** A hidden edge never counts, so a refusal depends
|
|
196
|
+
* only on edges the caller can read: it can't tell a node holding hidden
|
|
197
|
+
* edges from one holding none.
|
|
198
|
+
* - **A tombstoned partner frees its slot.** An edge whose other end is
|
|
199
|
+
* tombstoned stops counting at once, so retiring a term in a
|
|
200
|
+
* single-choice taxonomy doesn't freeze the nodes it classified.
|
|
201
|
+
* - **The limit is per caller, and can be overshot.** A node may hold
|
|
202
|
+
* more than `maxEdges` stored edges: edges a writer can't read don't
|
|
203
|
+
* count against that writer, and a `restore` of a tombstoned partner
|
|
204
|
+
* brings its edge back without a check (a restore is a chain write and
|
|
205
|
+
* reads no edge). An over-limit node keeps its edges; the next create
|
|
206
|
+
* at it fails until enough are deleted. Only a caller who can read
|
|
207
|
+
* every edge at the node (the view-private rung, typically) is held to
|
|
208
|
+
* the limit over all of them.
|
|
209
|
+
*/
|
|
210
|
+
readonly maxEdges?: number | undefined;
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* A family's ends, keyed by alias. The alias is how a list filter names an
|
|
214
|
+
* end's node columns ({@link endpointColumn}), the name under which the store
|
|
215
|
+
* joins that end's view, and the `end` an {@link EdgeCardinalityExceeded}
|
|
216
|
+
* reports. An alias never contains a `.` ({@link defineEdgeOps} throws
|
|
217
|
+
* {@link EdgeDeclarationError} on one), because `alias.column` must name one
|
|
218
|
+
* end's column unambiguously.
|
|
219
|
+
*
|
|
220
|
+
* @since 0.1.0
|
|
221
|
+
*/
|
|
222
|
+
export type EdgeEnds<E, R> = {
|
|
223
|
+
readonly [alias: string]: EdgeEnd<E, R>;
|
|
224
|
+
};
|
|
225
|
+
/**
|
|
226
|
+
* The list-filter column naming `column` of the node at end `alias`:
|
|
227
|
+
* `alias.column`. {@link endpointFilter} writes these, a family's own list
|
|
228
|
+
* filter may too, and a store renders each against the view it joined under
|
|
229
|
+
* that alias.
|
|
230
|
+
*
|
|
231
|
+
* @since 0.1.0
|
|
232
|
+
*/
|
|
233
|
+
export declare const endpointColumn: (alias: string, column: string) => string;
|
|
234
|
+
/**
|
|
235
|
+
* Whether the viewer may read the node at one end (`EDGE-001`): it exists,
|
|
236
|
+
* it is live or the viewer holds the view-deleted rung (a tombstone is read
|
|
237
|
+
* as the leaf's own `get` reads it, pulpit#93), and its leaf's access admits
|
|
238
|
+
* the viewer, a tombstone's included. A reference node is
|
|
239
|
+
* readable when its view has no `visibility` column, the viewer holds the
|
|
240
|
+
* view-private rung, it is `public`, or it is `followers` and the viewer is,
|
|
241
|
+
* or follows, the account its leaf's declared `followersOfColumn` names (the
|
|
242
|
+
* predicate `defineSubtype`'s `get` runs, pulpit#92); an owned node through
|
|
243
|
+
* `canViewOwned`'s predicate, where an owner column holding no string reads
|
|
244
|
+
* as no owner, matching no viewer and never asked of the follower seam. An
|
|
245
|
+
* owned declaration naming a column the view lacks reads nothing. Never
|
|
246
|
+
* fails.
|
|
247
|
+
*
|
|
248
|
+
* @since 0.1.0
|
|
249
|
+
*/
|
|
250
|
+
export declare const canViewEndpoint: <E, R>(end: EdgeEnd<E, R>, viewer: Viewer, policy: AccessPolicy, followers: FollowerSeam<unknown, R>, node: Option.Option<LeafRow>) => Effect.Effect<boolean, never, R>;
|
|
251
|
+
/**
|
|
252
|
+
* The list arm of `EDGE-001`: for every end, its node is live (or the viewer
|
|
253
|
+
* holds the view-deleted rung) and readable by the viewer, over the columns
|
|
254
|
+
* {@link endpointColumn} names. The same facts as {@link canViewEndpoint},
|
|
255
|
+
* rendered as a `ListFilter` — the owned arm is `ownedListFilter`, the
|
|
256
|
+
* reference arm `referencePrivacyFilter`'s, followers arm included
|
|
257
|
+
* (pulpit#92) — so the two never disagree. A
|
|
258
|
+
* family with no ends gets `always`.
|
|
259
|
+
*
|
|
260
|
+
* @since 0.1.0
|
|
261
|
+
*/
|
|
262
|
+
export declare const endpointFilter: <Following, E, R>(ends: EdgeEnds<E, R>, viewer: Viewer, policy: AccessPolicy, followers: FollowerSeam<Following, unknown>) => ListFilter<Following>;
|
|
263
|
+
/**
|
|
264
|
+
* What a write gate is asked about: the viewer, the application's role axes,
|
|
265
|
+
* and the edge being written (a create's column values, or a delete's stored
|
|
266
|
+
* row).
|
|
267
|
+
*
|
|
268
|
+
* @since 0.1.0
|
|
269
|
+
*/
|
|
270
|
+
export interface EdgeWriteRequest {
|
|
271
|
+
/** The request's viewer. */
|
|
272
|
+
readonly viewer: Viewer;
|
|
273
|
+
/** The application's role axes. */
|
|
274
|
+
readonly policy: AccessPolicy;
|
|
275
|
+
/** The edge's column values: the create's `values(input)`, or the stored row a delete removes. */
|
|
276
|
+
readonly edge: EdgeValues;
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
279
|
+
* The gate `create` and `delete` run after the ends' visibility check. It
|
|
280
|
+
* fails `Unauthorized` / `Forbidden`, or `NodeNotFound` when it reads an end
|
|
281
|
+
* (an owned-child gate on a node with no owner), or with the store's `E`.
|
|
282
|
+
*
|
|
283
|
+
* @since 0.1.0
|
|
284
|
+
*/
|
|
285
|
+
export type EdgeWriteGate<E, R> = (request: EdgeWriteRequest) => Effect.Effect<unknown, Unauthorized | Forbidden | NodeNotFound | E, R>;
|
|
286
|
+
/**
|
|
287
|
+
* A gate on one rung of the role axes: anonymous fails `Unauthorized`, a
|
|
288
|
+
* signed-in viewer below the rung `Forbidden`. `rungGate((policy) =>
|
|
289
|
+
* policy.canModerate)` is the moderator rung gardeneria's CMS edges take.
|
|
290
|
+
*
|
|
291
|
+
* @since 0.1.0
|
|
292
|
+
*/
|
|
293
|
+
export declare const rungGate: (rung: (policy: AccessPolicy) => (account: ViewerAccount) => boolean) => (request: EdgeWriteRequest) => Effect.Effect<ViewerAccount, Unauthorized | Forbidden>;
|
|
294
|
+
/**
|
|
295
|
+
* The default write gate: the corpus-write rung. Right for an edge joining
|
|
296
|
+
* reference rows, which is itself reference data.
|
|
297
|
+
*
|
|
298
|
+
* @since 0.1.0
|
|
299
|
+
*/
|
|
300
|
+
export declare const corpusWriteGate: (request: EdgeWriteRequest) => Effect.Effect<ViewerAccount, Unauthorized | Forbidden>;
|
|
301
|
+
/**
|
|
302
|
+
* An edge's column values by name, as a create writes them.
|
|
303
|
+
*
|
|
304
|
+
* @since 0.1.0
|
|
305
|
+
*/
|
|
306
|
+
export type EdgeValues = {
|
|
307
|
+
readonly [column: string]: ColumnValue;
|
|
308
|
+
};
|
|
309
|
+
/**
|
|
310
|
+
* A stored edge: its id, its creation instant (the list's order), and its columns.
|
|
311
|
+
*
|
|
312
|
+
* @since 0.1.0
|
|
313
|
+
*/
|
|
314
|
+
export interface EdgeRow {
|
|
315
|
+
readonly id: string;
|
|
316
|
+
readonly createdAt: Date;
|
|
317
|
+
readonly [column: string]: ColumnValue;
|
|
318
|
+
}
|
|
319
|
+
/**
|
|
320
|
+
* The persistence seam over one edge table. `E` is the store's own failure,
|
|
321
|
+
* `R` its requirements (a Drizzle store's `CurrentTransaction`: like the
|
|
322
|
+
* chain store, it never opens a transaction). Every method applies no policy.
|
|
323
|
+
*
|
|
324
|
+
* @since 0.1.0
|
|
325
|
+
*/
|
|
326
|
+
export interface EdgeStore<Row extends EdgeRow, Following, E, R> {
|
|
327
|
+
/** One edge by id, or none (`LIMIT 1`). */
|
|
328
|
+
readonly get: (id: string) => Effect.Effect<Option.Option<Row>, E, R>;
|
|
329
|
+
/**
|
|
330
|
+
* The page under `where`, with the unpaged total. Each end's
|
|
331
|
+
* current-version view is INNER JOINed on `view.id = edge.<end column>`
|
|
332
|
+
* under the end's alias, so `alias.column` names that view's column and a
|
|
333
|
+
* bare column the edge table's; an edge whose end has no row in its view
|
|
334
|
+
* is never listed. Ordered by `createdAt`, then `id` (a total order, so a
|
|
335
|
+
* page cursor cannot skip or repeat a row, #1231). The total is a window
|
|
336
|
+
* column (`count(*) OVER ()`): exact whenever the page has a row, and `0`
|
|
337
|
+
* for an empty page, a page past the end included.
|
|
338
|
+
*/
|
|
339
|
+
readonly list: (query: {
|
|
340
|
+
readonly where: ListFilter<Following>;
|
|
341
|
+
readonly limit: number;
|
|
342
|
+
readonly offset: number;
|
|
343
|
+
}) => Effect.Effect<Page<Row>, E, R>;
|
|
344
|
+
/**
|
|
345
|
+
* How many stored edges hold `nodeId` in `column` and satisfy `where`,
|
|
346
|
+
* over the same joins as {@link EdgeStore.list} (so `where` reads
|
|
347
|
+
* `alias.column` exactly as a list's does). The factory passes the
|
|
348
|
+
* viewer's `EDGE-001` filter and every end's liveness, so only edges the
|
|
349
|
+
* caller can read with live ends are counted (pulpit#77, pulpit#93).
|
|
350
|
+
* Called inside the create's transaction, for the cardinality check, once
|
|
351
|
+
* per bounded end in ascending `nodeId` order. A SQL store locks the node
|
|
352
|
+
* first (`FOR NO KEY UPDATE`, under READ COMMITTED), so two creates at one
|
|
353
|
+
* node serialise, and the sorted order means two creates locking the same
|
|
354
|
+
* pair of nodes cannot deadlock (README → Seams).
|
|
355
|
+
*/
|
|
356
|
+
readonly countAt: (query: {
|
|
357
|
+
readonly column: string;
|
|
358
|
+
readonly nodeId: string;
|
|
359
|
+
readonly where: ListFilter<Following>;
|
|
360
|
+
}) => Effect.Effect<number, E, R>;
|
|
361
|
+
/** The id of the stored edge `values` would collide with on the family's unique key, or none (always none without one). */
|
|
362
|
+
readonly existing: (values: EdgeValues) => Effect.Effect<Option.Option<string>, E, R>;
|
|
363
|
+
/** Insert the edge (`ON CONFLICT DO NOTHING RETURNING id`): its new id, or none when the unique key was taken. */
|
|
364
|
+
readonly insert: (values: EdgeValues) => Effect.Effect<Option.Option<string>, E, R>;
|
|
365
|
+
/** Hard-delete the edge; whether a row went. */
|
|
366
|
+
readonly delete: (id: string) => Effect.Effect<boolean, E, R>;
|
|
367
|
+
/**
|
|
368
|
+
* Set the edge's validity end column to `to` and update nothing else
|
|
369
|
+
* (pulpit#210, `EDGE-002`): `UPDATE … SET <column> = $to WHERE id = $id
|
|
370
|
+
* RETURNING id`; whether a row was updated. Required only by a family that
|
|
371
|
+
* declares {@link EdgeDefinition.validity}.
|
|
372
|
+
*/
|
|
373
|
+
readonly closeValidity?: CloseValidity<E, R> | undefined;
|
|
374
|
+
/**
|
|
375
|
+
* The node ids at `column` of the stored edges whose `otherColumn` is one
|
|
376
|
+
* of `ids` and that satisfy `where`, over the same joins as
|
|
377
|
+
* {@link EdgeStore.list}, as the follower seam's `Following` (pulpit#113):
|
|
378
|
+
* the subquery `SELECT e.<column> FROM <edge> e JOIN <each end's view> …
|
|
379
|
+
* WHERE e.<otherColumn> = ANY($ids) AND <where>` in SQL, never a
|
|
380
|
+
* materialised array, and the set itself in memory. `ids` is never empty
|
|
381
|
+
* and never longer than `MAX_SET_VALUES`: {@link EdgeOps.memberFilter}
|
|
382
|
+
* answers `never` (and {@link EdgeOps.nonMemberFilter} `always`) without asking.
|
|
383
|
+
*/
|
|
384
|
+
readonly members: (query: {
|
|
385
|
+
readonly column: string;
|
|
386
|
+
readonly otherColumn: string;
|
|
387
|
+
readonly ids: ReadonlyArray<string>;
|
|
388
|
+
readonly where: ListFilter<Following>;
|
|
389
|
+
}) => Effect.Effect<Following, E, R>;
|
|
390
|
+
/**
|
|
391
|
+
* How many stored edges hold each of `ids` in `column` and satisfy
|
|
392
|
+
* `where`, over the same joins as {@link EdgeStore.list} (pulpit#185): one
|
|
393
|
+
* `SELECT e.<column>, count(*) FROM <edge> e JOIN <each end's view> …
|
|
394
|
+
* WHERE e.<column> = ANY($ids) AND <where> GROUP BY e.<column>`, never a
|
|
395
|
+
* read per id. A node with no such edge has no key. `ids` is never empty,
|
|
396
|
+
* holds no duplicate and is never longer than `MAX_SET_VALUES`:
|
|
397
|
+
* {@link EdgeOps.countBy} answers without asking otherwise.
|
|
398
|
+
*/
|
|
399
|
+
readonly countBy: (query: {
|
|
400
|
+
readonly column: string;
|
|
401
|
+
readonly ids: ReadonlyArray<string>;
|
|
402
|
+
readonly where: ListFilter<Following>;
|
|
403
|
+
}) => Effect.Effect<HashMap.HashMap<string, number>, E, R>;
|
|
404
|
+
}
|
|
405
|
+
/**
|
|
406
|
+
* An edge family's declaration: its type and ends, its seams, its create
|
|
407
|
+
* mapping, conflict policy and write gate, and its row → resource mapper.
|
|
408
|
+
*
|
|
409
|
+
* @since 0.1.0
|
|
410
|
+
*/
|
|
411
|
+
export interface EdgeDefinition<Create, Row extends EdgeRow, Resource, Following, E, R> {
|
|
412
|
+
/** The family's JSON:API wire type: names spans and tags `EdgeNotFound`. */
|
|
413
|
+
readonly type: string;
|
|
414
|
+
/** The family's ends by alias; each node must be readable for its edge to be. */
|
|
415
|
+
readonly ends: EdgeEnds<E, R>;
|
|
416
|
+
/** The edge table. */
|
|
417
|
+
readonly edges: EdgeStore<Row, Following, E, R>;
|
|
418
|
+
/** The request's viewer (effect-auth's session context). */
|
|
419
|
+
readonly viewer: Effect.Effect<Viewer, never, R>;
|
|
420
|
+
/** The application's role axes. */
|
|
421
|
+
readonly policy: AccessPolicy;
|
|
422
|
+
/** The follower seam the owned ends read; `nobodyFollows` when the runtime provides none. */
|
|
423
|
+
readonly followers: FollowerSeam<Following, R>;
|
|
424
|
+
/** The edge's column values from the create input; each end's column must hold its node id. */
|
|
425
|
+
readonly values: (input: Create) => EdgeValues;
|
|
426
|
+
/** Shape a stored edge into the resource. */
|
|
427
|
+
readonly toResource: (row: Row) => Resource;
|
|
428
|
+
/**
|
|
429
|
+
* A create colliding with a stored edge on the unique key: `"fail"`
|
|
430
|
+
* ({@link EdgeConflict}, the default) or `"ignore"` (re-assertion is a
|
|
431
|
+
* no-op returning the stored edge's id, when the viewer may read it and
|
|
432
|
+
* its every end is live; otherwise {@link EdgeConflict}).
|
|
433
|
+
*/
|
|
434
|
+
readonly onConflict?: "fail" | "ignore" | undefined;
|
|
435
|
+
/** The write gate; {@link corpusWriteGate} when absent. */
|
|
436
|
+
readonly writeGate?: EdgeWriteGate<E, R> | undefined;
|
|
437
|
+
/**
|
|
438
|
+
* The family's validity interval (pulpit#210): the edge columns holding
|
|
439
|
+
* when it starts and ends to hold (each a `Date`, or `NULL` for open).
|
|
440
|
+
* With it, a create checks `from ≤ to`, `maxEdges` counts only the edges
|
|
441
|
+
* valid at the same time as the new one, and `closeValidity` may end an
|
|
442
|
+
* open interval. Absent, edges have no interval.
|
|
443
|
+
*/
|
|
444
|
+
readonly validity?: EdgeValidity | undefined;
|
|
445
|
+
}
|
|
446
|
+
/**
|
|
447
|
+
* {@link EdgeStore.closeValidity}'s shape: set the edge `id`'s `column` to
|
|
448
|
+
* `to`; whether a row was updated.
|
|
449
|
+
*
|
|
450
|
+
* @since 0.1.0
|
|
451
|
+
*/
|
|
452
|
+
export type CloseValidity<E, R> = (id: string, column: string, to: Date) => Effect.Effect<boolean, E, R>;
|
|
453
|
+
/**
|
|
454
|
+
* An edge family's validity interval columns (pulpit#210). An edge holds
|
|
455
|
+
* over `[from, to)`: from its `from` instant (or always, when `NULL`) until
|
|
456
|
+
* its `to` instant (or forever, when `NULL`).
|
|
457
|
+
*
|
|
458
|
+
* @since 0.1.0
|
|
459
|
+
*/
|
|
460
|
+
export interface EdgeValidity {
|
|
461
|
+
readonly from: string;
|
|
462
|
+
readonly to: string;
|
|
463
|
+
}
|
|
464
|
+
declare const InvalidValidity_base: Schema.Class<InvalidValidity, Schema.TaggedStruct<"InvalidValidity", {
|
|
465
|
+
readonly type: Schema.String;
|
|
466
|
+
readonly reason: Schema.String;
|
|
467
|
+
}>, import("effect/Cause").YieldableError & {
|
|
468
|
+
readonly brand: unique symbol;
|
|
469
|
+
}>;
|
|
470
|
+
/**
|
|
471
|
+
* An edge's validity interval is malformed: a bound that is neither a
|
|
472
|
+
* `Date` nor `NULL`, `from` after `to`, or a close that would widen it. The
|
|
473
|
+
* wire's 422.
|
|
474
|
+
*
|
|
475
|
+
* @since 0.1.0
|
|
476
|
+
*/
|
|
477
|
+
export declare class InvalidValidity extends InvalidValidity_base {
|
|
478
|
+
}
|
|
479
|
+
/**
|
|
480
|
+
* The edges valid at `at` (pulpit#210), as a list filter over a family's
|
|
481
|
+
* {@link EdgeValidity} columns: `from` is `NULL` or at or before `at`, and
|
|
482
|
+
* `to` is `NULL` or after it. Pass it as a list's `where`, so "who held
|
|
483
|
+
* the office on that day" is one gated read.
|
|
484
|
+
*
|
|
485
|
+
* @example
|
|
486
|
+
* ```ts
|
|
487
|
+
* import { strictEqual } from "node:assert";
|
|
488
|
+
* import { validAt } from "@thomasfosterau/pulpit/edges";
|
|
489
|
+
* import { evaluate } from "@thomasfosterau/pulpit/filter";
|
|
490
|
+
*
|
|
491
|
+
* const filter = validAt({ from: "validFrom", to: "validTo" }, new Date("2020-06-01"));
|
|
492
|
+
* const context = { now: new Date(0), contains: () => false };
|
|
493
|
+
* const row = { validFrom: new Date("2020-01-01"), validTo: null };
|
|
494
|
+
* strictEqual(evaluate(filter, row, context), true);
|
|
495
|
+
* strictEqual(evaluate(filter, { ...row, validTo: new Date("2020-03-01") }, context), false);
|
|
496
|
+
* ```
|
|
497
|
+
*
|
|
498
|
+
* @since 0.1.0
|
|
499
|
+
*/
|
|
500
|
+
export declare const validAt: <Following = never>(validity: EdgeValidity, at: Date) => ListFilter<Following>;
|
|
501
|
+
/**
|
|
502
|
+
* A list request: the bounded page cursor and the family's own predicate.
|
|
503
|
+
*
|
|
504
|
+
* @since 0.1.0
|
|
505
|
+
*/
|
|
506
|
+
export interface EdgeListRequest {
|
|
507
|
+
readonly limit: number;
|
|
508
|
+
readonly offset: number;
|
|
509
|
+
/** The family's predicate over edge columns (and {@link endpointColumn}s), or `always`. */
|
|
510
|
+
readonly where: ListFilter<never>;
|
|
511
|
+
}
|
|
512
|
+
/**
|
|
513
|
+
* A create's outcome: the edge's id, and whether this call wrote it (`false`
|
|
514
|
+
* when an `"ignore"` family resolved a re-assertion to the stored edge).
|
|
515
|
+
*
|
|
516
|
+
* @since 0.1.0
|
|
517
|
+
*/
|
|
518
|
+
export interface EdgeWrite {
|
|
519
|
+
readonly id: string;
|
|
520
|
+
readonly created: boolean;
|
|
521
|
+
}
|
|
522
|
+
/**
|
|
523
|
+
* The operations {@link defineEdgeOps} produces for one edge family.
|
|
524
|
+
*
|
|
525
|
+
* @since 0.1.0
|
|
526
|
+
*/
|
|
527
|
+
export interface EdgeOps<Create, Resource, E, R, Following = never> {
|
|
528
|
+
/**
|
|
529
|
+
* One page of the edges the viewer may read, with the unpaged total as the
|
|
530
|
+
* store reports it (`EDGE-001`: an edge with a hidden or tombstoned end is
|
|
531
|
+
* omitted). The limit is clamped to `MAX_PAGE_LIMIT` (`SEC-INPUT-003`).
|
|
532
|
+
* Fails only with the store's `E`.
|
|
533
|
+
*/
|
|
534
|
+
readonly list: (request: EdgeListRequest) => Effect.Effect<Page<Resource>, E, R>;
|
|
535
|
+
/**
|
|
536
|
+
* The edge as a resource. Fails `EdgeNotFound` when it is missing OR an
|
|
537
|
+
* end is hidden from the viewer (`SEC-AUTH-ENUM-001`, `EDGE-001`).
|
|
538
|
+
*/
|
|
539
|
+
readonly get: (id: string) => Effect.Effect<Resource, EdgeNotFound | E, R>;
|
|
540
|
+
/**
|
|
541
|
+
* Assert the edge. Fails `NodeNotFound` when an end names a node that is
|
|
542
|
+
* missing, tombstoned or hidden from the viewer (checked before the gate,
|
|
543
|
+
* so a 403 never confirms a node; a tombstone is `NodeNotFound` to the
|
|
544
|
+
* view-deleted rung too); the gate's `Unauthorized` / `Forbidden` (or
|
|
545
|
+
* `NodeNotFound`); `EdgeConflict` on a unique-key collision the conflict
|
|
546
|
+
* policy does not resolve; `EdgeCardinalityExceeded` when an end's node
|
|
547
|
+
* already holds its `maxEdges` edges that the caller can read with every
|
|
548
|
+
* end live. Under `"fail"` a collision with a hidden edge answers exactly
|
|
549
|
+
* as one with a readable edge; under `"ignore"` only a readable, live edge
|
|
550
|
+
* resolves (to `{ created: false, id }`), and any other collision is
|
|
551
|
+
* `EdgeConflict` (`SEC-AUTH-ENUM-001`, pulpit#93). The count reads no
|
|
552
|
+
* hidden edge ({@link EdgeEnd.maxEdges}). An `EdgeConflict` still says the
|
|
553
|
+
* key is taken, which only a family whose unique key leaves an end out
|
|
554
|
+
* can say about a hidden edge (README → `./edges`).
|
|
555
|
+
*/
|
|
556
|
+
readonly create: (input: Create) => Effect.Effect<EdgeWrite, Unauthorized | Forbidden | NodeNotFound | EdgeConflict | EdgeCardinalityExceeded | InvalidValidity | E, R>;
|
|
557
|
+
/**
|
|
558
|
+
* End the edge's validity interval at `at` (pulpit#210): the one update an
|
|
559
|
+
* edge takes (`EDGE-002`), which sets only the interval's `to` column and
|
|
560
|
+
* only narrows it: `at` must not be after a closed interval's end, nor
|
|
561
|
+
* before its start. Fails `EdgeNotFound` for a missing or hidden edge
|
|
562
|
+
* (before the gate), the gate's failures, and {@link InvalidValidity} for
|
|
563
|
+
* a family without an interval or an `at` that would widen it.
|
|
564
|
+
*/
|
|
565
|
+
readonly closeValidity: (id: string, at: Date) => Effect.Effect<{
|
|
566
|
+
readonly id: string;
|
|
567
|
+
}, Unauthorized | Forbidden | NodeNotFound | EdgeNotFound | InvalidValidity | E, R>;
|
|
568
|
+
/**
|
|
569
|
+
* Hard-delete the edge. Fails `EdgeNotFound` when it is missing or hidden
|
|
570
|
+
* from the viewer (checked before the gate; the view-deleted rung reads an
|
|
571
|
+
* edge whose only hidden end is tombstoned, so it can delete one), then
|
|
572
|
+
* the gate's failures.
|
|
573
|
+
*/
|
|
574
|
+
readonly delete: (id: string) => Effect.Effect<{
|
|
575
|
+
readonly id: string;
|
|
576
|
+
}, Unauthorized | Forbidden | NodeNotFound | EdgeNotFound | E, R>;
|
|
577
|
+
/**
|
|
578
|
+
* Edge membership as a list filter for the leaf at `end` (pulpit#113):
|
|
579
|
+
* `id IN (<the end's node ids of the edges whose other end is one of
|
|
580
|
+
* otherIds>)`, so `?categories=1,2` lists the posts holding a
|
|
581
|
+
* `post_category` edge to one of those terms. Only edges the viewer can
|
|
582
|
+
* read count (`EDGE-001`, the same `endpointFilter` as {@link list}): an
|
|
583
|
+
* edge with a hidden end, or a tombstoned end below the view-deleted
|
|
584
|
+
* rung, never makes a row a member, so a hidden term's edges are never
|
|
585
|
+
* confirmed. The filter is the store's `members` subquery under
|
|
586
|
+
* `./filter`'s `in` node, so a `NULL` id is never a member.
|
|
587
|
+
*
|
|
588
|
+
* `end` names the member end by alias, and the family's one other end is
|
|
589
|
+
* the end `otherIds` name. Fails closed, as `never`, without reading the
|
|
590
|
+
* store: no `otherIds`, more than `MAX_SET_VALUES` of them
|
|
591
|
+
* (`SEC-INPUT-003`), an `end` the family doesn't declare, or a family
|
|
592
|
+
* without exactly two ends. Fails only with the store's `E`.
|
|
593
|
+
*/
|
|
594
|
+
readonly memberFilter: (end: string, otherIds: ReadonlyArray<string>) => Effect.Effect<ListFilter<Following>, E, R>;
|
|
595
|
+
/**
|
|
596
|
+
* Edge non-membership as a list filter for the leaf at `end` (pulpit#191):
|
|
597
|
+
* the complement of {@link EdgeOps.memberFilter} over the same store
|
|
598
|
+
* `members` subquery, `(id IS NULL OR id NOT IN (…))` under `./filter`'s
|
|
599
|
+
* `notIn` node, so `?categories_exclude=1,2` lists the posts holding no
|
|
600
|
+
* readable `post_category` edge to those terms. The member set and this
|
|
601
|
+
* filter partition the rows a leaf's `list` reads.
|
|
602
|
+
*
|
|
603
|
+
* Only edges the viewer can read count as a membership (`EDGE-001`): a row
|
|
604
|
+
* whose only edge to `otherIds` is hidden from the viewer, or has a
|
|
605
|
+
* tombstoned end below the view-deleted rung, is not excluded, so the
|
|
606
|
+
* hidden edge is never confirmed. An exclusion fails closed by excluding
|
|
607
|
+
* nothing: no `otherIds`, more than `MAX_SET_VALUES` of them
|
|
608
|
+
* (`SEC-INPUT-003`; an adapter caps its list before this), an `end` the
|
|
609
|
+
* family doesn't declare, or a family without exactly two ends all give
|
|
610
|
+
* `always` without reading the store. Fails only with the store's `E`.
|
|
611
|
+
*/
|
|
612
|
+
readonly nonMemberFilter: (end: string, otherIds: ReadonlyArray<string>) => Effect.Effect<ListFilter<Following>, E, R>;
|
|
613
|
+
/**
|
|
614
|
+
* The number of edges the viewer can read at each node of `ids` at end
|
|
615
|
+
* `end` (pulpit#185), in one grouped store read: a taxonomy term's post
|
|
616
|
+
* count, for every term on a page. Only edges {@link list} would show
|
|
617
|
+
* count (`EDGE-001`, the same `endpointFilter`, and `where`, the family's
|
|
618
|
+
* own predicate, as {@link EdgeListRequest.where}): an edge with a hidden
|
|
619
|
+
* end, or a tombstoned end below the view-deleted rung, never counts, so a
|
|
620
|
+
* hidden or tombstoned node at `end` counts `0` and its edges are never
|
|
621
|
+
* confirmed.
|
|
622
|
+
*
|
|
623
|
+
* Every requested id (deduplicated) has a key, `0` for a node with no
|
|
624
|
+
* readable edge. Fails closed, every count `0` without reading the store,
|
|
625
|
+
* for more than `MAX_SET_VALUES` ids (`SEC-INPUT-003`) or an `end` the
|
|
626
|
+
* family doesn't declare; no ids read nothing. Fails only with the
|
|
627
|
+
* store's `E`.
|
|
628
|
+
*/
|
|
629
|
+
readonly countBy: (end: string, ids: ReadonlyArray<string>, where?: ListFilter<never> | undefined) => Effect.Effect<HashMap.HashMap<string, number>, E, R>;
|
|
630
|
+
}
|
|
631
|
+
/**
|
|
632
|
+
* Bind an edge family's operations. Every produced operation is traced as
|
|
633
|
+
* `${type}.<operation>`; the writes run in the caller's transaction (the
|
|
634
|
+
* boundary is the caller's, website #1883), and a create's cardinality
|
|
635
|
+
* count and insert share it (README → Seams).
|
|
636
|
+
*
|
|
637
|
+
* Throws {@link EdgeDeclarationError}, when the family is declared, for an
|
|
638
|
+
* end alias containing a `.` or a `maxEdges` that is not a positive safe
|
|
639
|
+
* integer.
|
|
640
|
+
*
|
|
641
|
+
* @since 0.1.0
|
|
642
|
+
*/
|
|
643
|
+
export declare const defineEdgeOps: <Create, Row extends EdgeRow, Resource, Following, E, R>(definition: EdgeDefinition<Create, Row, Resource, Following, E, R>) => EdgeOps<Create, Resource, E, R, Following>;
|
|
644
|
+
export {};
|
|
645
|
+
//# sourceMappingURL=edges.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"edges.d.ts","sourceRoot":"","sources":["../src/edges.ts"],"names":[],"mappings":"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"}
|