@thomasfosterau/pulpit 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.
Files changed (143) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +1272 -2
  3. package/dist/blobs.d.ts +249 -0
  4. package/dist/blobs.d.ts.map +1 -0
  5. package/dist/blobs.js +193 -0
  6. package/dist/blobs.js.map +1 -0
  7. package/dist/budgets.d.ts +255 -0
  8. package/dist/budgets.d.ts.map +1 -0
  9. package/dist/budgets.js +211 -0
  10. package/dist/budgets.js.map +1 -0
  11. package/dist/cache.d.ts +154 -0
  12. package/dist/cache.d.ts.map +1 -0
  13. package/dist/cache.js +118 -0
  14. package/dist/cache.js.map +1 -0
  15. package/dist/collections.d.ts +242 -0
  16. package/dist/collections.d.ts.map +1 -0
  17. package/dist/collections.js +215 -0
  18. package/dist/collections.js.map +1 -0
  19. package/dist/comments.d.ts +212 -0
  20. package/dist/comments.d.ts.map +1 -0
  21. package/dist/comments.js +192 -0
  22. package/dist/comments.js.map +1 -0
  23. package/dist/content.d.ts +231 -0
  24. package/dist/content.d.ts.map +1 -0
  25. package/dist/content.js +204 -0
  26. package/dist/content.js.map +1 -0
  27. package/dist/documents.d.ts +184 -0
  28. package/dist/documents.d.ts.map +1 -0
  29. package/dist/documents.js +190 -0
  30. package/dist/documents.js.map +1 -0
  31. package/dist/edges.d.ts +645 -0
  32. package/dist/edges.d.ts.map +1 -0
  33. package/dist/edges.js +558 -0
  34. package/dist/edges.js.map +1 -0
  35. package/dist/filter.d.ts +461 -0
  36. package/dist/filter.d.ts.map +1 -0
  37. package/dist/filter.js +387 -0
  38. package/dist/filter.js.map +1 -0
  39. package/dist/index.d.ts +133 -0
  40. package/dist/index.d.ts.map +1 -0
  41. package/dist/index.js +133 -0
  42. package/dist/index.js.map +1 -0
  43. package/dist/internal/filter-columns.d.ts +8 -0
  44. package/dist/internal/filter-columns.d.ts.map +1 -0
  45. package/dist/internal/filter-columns.js +43 -0
  46. package/dist/internal/filter-columns.js.map +1 -0
  47. package/dist/internal/list-order.d.ts +26 -0
  48. package/dist/internal/list-order.d.ts.map +1 -0
  49. package/dist/internal/list-order.js +92 -0
  50. package/dist/internal/list-order.js.map +1 -0
  51. package/dist/internal/owned-columns.d.ts +93 -0
  52. package/dist/internal/owned-columns.d.ts.map +1 -0
  53. package/dist/internal/owned-columns.js +98 -0
  54. package/dist/internal/owned-columns.js.map +1 -0
  55. package/dist/internal/plain-rows.d.ts +32 -0
  56. package/dist/internal/plain-rows.d.ts.map +1 -0
  57. package/dist/internal/plain-rows.js +32 -0
  58. package/dist/internal/plain-rows.js.map +1 -0
  59. package/dist/internal/reference-columns.d.ts +80 -0
  60. package/dist/internal/reference-columns.d.ts.map +1 -0
  61. package/dist/internal/reference-columns.js +102 -0
  62. package/dist/internal/reference-columns.js.map +1 -0
  63. package/dist/moderation.d.ts +392 -0
  64. package/dist/moderation.d.ts.map +1 -0
  65. package/dist/moderation.js +375 -0
  66. package/dist/moderation.js.map +1 -0
  67. package/dist/paths.d.ts +204 -0
  68. package/dist/paths.d.ts.map +1 -0
  69. package/dist/paths.js +165 -0
  70. package/dist/paths.js.map +1 -0
  71. package/dist/proposals.d.ts +523 -0
  72. package/dist/proposals.d.ts.map +1 -0
  73. package/dist/proposals.js +505 -0
  74. package/dist/proposals.js.map +1 -0
  75. package/dist/publication.d.ts +148 -0
  76. package/dist/publication.d.ts.map +1 -0
  77. package/dist/publication.js +183 -0
  78. package/dist/publication.js.map +1 -0
  79. package/dist/purge.d.ts +224 -0
  80. package/dist/purge.d.ts.map +1 -0
  81. package/dist/purge.js +151 -0
  82. package/dist/purge.js.map +1 -0
  83. package/dist/reactions.d.ts +246 -0
  84. package/dist/reactions.d.ts.map +1 -0
  85. package/dist/reactions.js +183 -0
  86. package/dist/reactions.js.map +1 -0
  87. package/dist/registry.d.ts +316 -0
  88. package/dist/registry.d.ts.map +1 -0
  89. package/dist/registry.js +305 -0
  90. package/dist/registry.js.map +1 -0
  91. package/dist/search.d.ts +366 -0
  92. package/dist/search.d.ts.map +1 -0
  93. package/dist/search.js +273 -0
  94. package/dist/search.js.map +1 -0
  95. package/dist/social.d.ts +311 -0
  96. package/dist/social.d.ts.map +1 -0
  97. package/dist/social.js +205 -0
  98. package/dist/social.js.map +1 -0
  99. package/dist/subtype.d.ts +1613 -0
  100. package/dist/subtype.d.ts.map +1 -0
  101. package/dist/subtype.js +1300 -0
  102. package/dist/subtype.js.map +1 -0
  103. package/dist/testing.d.ts +675 -0
  104. package/dist/testing.d.ts.map +1 -0
  105. package/dist/testing.js +1022 -0
  106. package/dist/testing.js.map +1 -0
  107. package/dist/version-chain.d.ts +702 -0
  108. package/dist/version-chain.d.ts.map +1 -0
  109. package/dist/version-chain.js +489 -0
  110. package/dist/version-chain.js.map +1 -0
  111. package/dist/working-copy.d.ts +116 -0
  112. package/dist/working-copy.d.ts.map +1 -0
  113. package/dist/working-copy.js +95 -0
  114. package/dist/working-copy.js.map +1 -0
  115. package/package.json +145 -3
  116. package/src/blobs.ts +384 -0
  117. package/src/budgets.ts +338 -0
  118. package/src/cache.ts +218 -0
  119. package/src/collections.ts +427 -0
  120. package/src/comments.ts +392 -0
  121. package/src/content.ts +238 -0
  122. package/src/documents.ts +284 -0
  123. package/src/edges.ts +1185 -0
  124. package/src/filter.ts +523 -0
  125. package/src/index.ts +153 -0
  126. package/src/internal/filter-columns.ts +50 -0
  127. package/src/internal/list-order.ts +117 -0
  128. package/src/internal/owned-columns.ts +140 -0
  129. package/src/internal/plain-rows.ts +54 -0
  130. package/src/internal/reference-columns.ts +156 -0
  131. package/src/moderation.ts +728 -0
  132. package/src/paths.ts +267 -0
  133. package/src/proposals.ts +1014 -0
  134. package/src/publication.ts +225 -0
  135. package/src/purge.ts +313 -0
  136. package/src/reactions.ts +347 -0
  137. package/src/registry.ts +524 -0
  138. package/src/search.ts +540 -0
  139. package/src/social.ts +469 -0
  140. package/src/subtype.ts +3095 -0
  141. package/src/testing.ts +1873 -0
  142. package/src/version-chain.ts +1093 -0
  143. package/src/working-copy.ts +192 -0
@@ -0,0 +1,675 @@
1
+ /**
2
+ * **In-memory fakes of the kernel's seams** — a store for each of the
3
+ * version-chain, leaf, document, edge, proposal, path, report, publication, purge and embedding seams, an
4
+ * endpoint view, a follower seam, a search branch (with a scope check) and a resource cache, all held in memory with no SQL. They are the reference semantics of
5
+ * each seam's contract in the package README → Seams, and the fakes the
6
+ * kernel's own suites run against; an adapter's or an application's tests can
7
+ * run the kernel's operations over them too.
8
+ *
9
+ * **Not for production.** Every store here keeps its rows in a closure for the
10
+ * life of the value, persists nothing, never opens a transaction and has no
11
+ * concurrency control beyond what a single JavaScript thread gives it. Each
12
+ * store enforces what the real table's schema enforces (a primary or unique
13
+ * key) and nothing the operations are responsible for, so a test of the
14
+ * operations over a fake exercises the operations' policy, not the store's.
15
+ * Each also carries a few test hooks (`rows`, `dropState`, `raceNextAppend`,
16
+ * …) that a real store has no counterpart for: they read what was written,
17
+ * or simulate a state a real database reaches only through a race or a
18
+ * missing row.
19
+ *
20
+ * The follower seam's `in` set is a `HashSet` of account ids
21
+ * ({@link InMemoryFollowing}) throughout, where a SQL store's is a subquery.
22
+ *
23
+ * @example
24
+ * ```ts
25
+ * import { deepStrictEqual } from "node:assert";
26
+ * import * as Effect from "effect/Effect";
27
+ * import * as Option from "effect/Option";
28
+ * import { defineVersionChain } from "@thomasfosterau/pulpit/version-chain";
29
+ * import { makeInMemoryChainStore } from "@thomasfosterau/pulpit/testing";
30
+ *
31
+ * const store = makeInMemoryChainStore();
32
+ * const chain = defineVersionChain("notes", store);
33
+ * const { id } = Effect.runSync(
34
+ * chain.create({ id: Option.none(), state: { title: "Hello" }, actorId: Option.none() }),
35
+ * );
36
+ *
37
+ * // The chain operations appended version 0, and the fake recorded it.
38
+ * deepStrictEqual(
39
+ * store.rows().map((row) => [row.id, row.version, row.action]),
40
+ * [[id, 0n, "create"]],
41
+ * );
42
+ * ```
43
+ *
44
+ * @copyright Thomas Foster
45
+ * @module pulpit/testing
46
+ * @since 0.1.0
47
+ */
48
+ import * as HashMap from "effect/HashMap";
49
+ import * as HashSet from "effect/HashSet";
50
+ import * as Option from "effect/Option";
51
+ import * as Schema from "effect/Schema";
52
+ import { type BlobHead, type BlobStore } from "./blobs.ts";
53
+ import type { BudgetCounter, BudgetRequest, WriteCountStore } from "./budgets.ts";
54
+ import { type DocumentHash, type DocumentStore } from "./documents.ts";
55
+ import { type EdgeEnds, type EdgeRow, type EdgeStore, type EdgeValues, type EndpointView } from "./edges.ts";
56
+ import { type FilterRow } from "./filter.ts";
57
+ import type { PurgeStore } from "./purge.ts";
58
+ import { SearchScopeLeak, type EmbeddingStore, type SearchBranch } from "./search.ts";
59
+ import type { ReactionRow, ReactionStore } from "./reactions.ts";
60
+ import type { KvEntry, VersionedKv } from "./cache.ts";
61
+ import type { CollectionEntry, CollectionStore } from "./collections.ts";
62
+ import type { Relationship, SocialStore } from "./social.ts";
63
+ import { type PathEntry, type PathStore } from "./paths.ts";
64
+ import type { ReportRow, ReportStore } from "./moderation.ts";
65
+ import type { ProposalRow, ProposalStore } from "./proposals.ts";
66
+ import type { WorkingCopy, WorkingCopyStore } from "./working-copy.ts";
67
+ import type { FollowerSeam, LeafRow, LeafStore, LiveView, PublicationFact, PublicationStore, ResourceCacheSeam } from "./subtype.ts";
68
+ import { type VersionChainStore, type VersionRow } from "./version-chain.ts";
69
+ /**
70
+ * The follower seam's `in` set in every fake here: the ids of the accounts a
71
+ * viewer follows. A SQL store's is a subquery; the in-memory list filters
72
+ * test membership in this set instead.
73
+ *
74
+ * @since 0.1.0
75
+ */
76
+ export type InMemoryFollowing = HashSet.HashSet<string>;
77
+ /**
78
+ * An in-memory {@link FollowerSeam} over an explicit table of who follows
79
+ * whom: each entry is a follower's id and the ids of the accounts it
80
+ * follows. `isFollower` answers from the table and `following` returns the
81
+ * follower's row as an {@link InMemoryFollowing} set (empty for an account the
82
+ * table doesn't name), the in-memory reading of effect-auth's `IsFollower`
83
+ * (README → Seams → the follower seam). In memory only; not for production.
84
+ *
85
+ * @since 0.1.0
86
+ */
87
+ export declare const makeInMemoryFollowers: (table: Iterable<readonly [follower: string, followed: ReadonlyArray<string>]>) => FollowerSeam<InMemoryFollowing, never>;
88
+ /**
89
+ * An in-memory {@link VersionChainStore}, plus the hooks a test reads and
90
+ * steers it through. In memory only; not for production.
91
+ *
92
+ * @since 0.1.0
93
+ */
94
+ export interface InMemoryChainStore extends VersionChainStore<never, never> {
95
+ /** Every row ever appended, in append order — what the append-only assertions read. */
96
+ readonly rows: () => ReadonlyArray<VersionRow>;
97
+ /** Simulate a partial chain: the state at `(id, version)` is no longer reconstructable. */
98
+ readonly dropState: (id: string, version: bigint) => void;
99
+ /** Simulate a concurrent writer: the next append for `id` finds its version taken. */
100
+ readonly raceNextAppend: (id: string) => void;
101
+ /**
102
+ * Hard-delete every row of `id` and return them: what
103
+ * {@link makeInMemoryPurgeStore} erases a node through, and nothing else
104
+ * may call (`VERSION-CHAIN-001/002`).
105
+ */
106
+ readonly erase: (id: string) => ReadonlyArray<VersionRow>;
107
+ }
108
+ /**
109
+ * An in-memory {@link VersionChainStore}: the seam's contract (README → Seams
110
+ * → `VersionChainStore`) over an append log. It enforces what the Drizzle
111
+ * store's schema enforces, the `(id, version)` primary key (an `append` at a
112
+ * taken pair fails `VersionConflict`), and nothing the operations are
113
+ * responsible for. `mintId` mints sequential UUID-shaped ids.
114
+ *
115
+ * `dropState` simulates a partial chain (a level with no row at a version),
116
+ * which a real database reaches through a missing leaf row, and
117
+ * `raceNextAppend` a writer that takes the next version first. In memory
118
+ * only; not for production.
119
+ *
120
+ * @since 0.1.0
121
+ */
122
+ export declare const makeInMemoryChainStore: () => InMemoryChainStore;
123
+ /**
124
+ * An in-memory {@link LeafStore} over an {@link InMemoryChainStore}, plus the
125
+ * hooks a test reads and steers its owner stamps through. In memory only; not
126
+ * for production.
127
+ *
128
+ * @since 0.1.0
129
+ */
130
+ export interface InMemoryLeafStore extends LeafStore<LeafRow, InMemoryFollowing, never, never> {
131
+ /** Every owner stamp, node id → owner id — what the owner-stamp assertions read. */
132
+ readonly owners: () => HashMap.HashMap<string, string>;
133
+ /** Simulate an ownerless owned node: drop `id`'s owner stamp (the apps' `core.node_owner` row). */
134
+ readonly dropOwner: (id: string) => void;
135
+ }
136
+ /**
137
+ * Options for {@link makeInMemoryLeafStore}.
138
+ *
139
+ * @since 0.1.0
140
+ */
141
+ export interface InMemoryLeafStoreOptions {
142
+ /** The view's leaf columns; `id`, `action` and `ownerId` are always in its `columns`. */
143
+ readonly columns: ReadonlyArray<string>;
144
+ /**
145
+ * A localised leaf's view (pulpit#56): its `columns` gain
146
+ * {@link LOCALE_COLUMN}, and `list` reads one row per `(id, locale)`.
147
+ * `get` / `getMany` read the shared row either way.
148
+ */
149
+ readonly localised?: boolean | undefined;
150
+ /** The instant `published` / `notPublished` compare against; the Effect `Clock`'s at each `list` when absent. */
151
+ readonly now?: Date | undefined;
152
+ }
153
+ /**
154
+ * An in-memory {@link LeafStore}: the seam's contract (README → Seams →
155
+ * `LeafStore`) as a current-version view over an {@link InMemoryChainStore}.
156
+ * Each node's row is its head version's state with its head `version` and `action`, and
157
+ * `ownerId` merged in from the owner stamps (the apps' `core.node_owner`
158
+ * join); `list` evaluates its {@link ListFilter} in memory, orders by its
159
+ * `orderBy` keys then `id` ascending as the seam's contract renders it
160
+ * (`NULLS LAST` in both directions, a string by code point as `COLLATE "C"`
161
+ * sorts it, a `Date` by its instant), and returns the unpaged total. Like a
162
+ * SQL store, it takes `orderBy` as already checked against `columns`. It
163
+ * applies no visibility policy, as a real view applies none. In memory only;
164
+ * not for production.
165
+ *
166
+ * @since 0.1.0
167
+ */
168
+ export declare const makeInMemoryLeafStore: (chain: InMemoryChainStore, options: InMemoryLeafStoreOptions) => InMemoryLeafStore;
169
+ /**
170
+ * An in-memory {@link PathStore}, plus the read a test checks it through. In
171
+ * memory only; not for production.
172
+ *
173
+ * @since 0.1.0
174
+ */
175
+ export interface InMemoryPathStore extends PathStore<never, never> {
176
+ /** Every stored path and its entry, aliases included. */
177
+ readonly entries: () => HashMap.HashMap<string, PathEntry>;
178
+ }
179
+ /**
180
+ * An in-memory {@link PathStore}: the seam's contract (README → Seams →
181
+ * `PathStore`) as one map from path to entry, its key the path table's
182
+ * primary key, so each path is stored once. `claim` fails {@link PathTaken}
183
+ * for another node's canonical path, replaces an alias at the path, and
184
+ * demotes the node's previous canonical path to its alias, in one step;
185
+ * `release` drops the node's canonical path and keeps its aliases. In memory
186
+ * only; not for production.
187
+ *
188
+ * @since 0.1.0
189
+ */
190
+ export declare const makeInMemoryPathStore: () => InMemoryPathStore;
191
+ /**
192
+ * An in-memory {@link ResourceCacheSeam}, plus the reads a test checks it
193
+ * through. In memory only; not for production.
194
+ *
195
+ * @since 0.1.0
196
+ */
197
+ export interface InMemoryResourceCache<Resource> extends ResourceCacheSeam<Resource, never> {
198
+ /** The resource the cache would serve for `id`: none when evicted, refused or never set. */
199
+ readonly get: (id: string) => Option.Option<Resource>;
200
+ /** The highest version the cache holds for `id`, set or evicted, or none. */
201
+ readonly versionOf: (id: string) => Option.Option<bigint>;
202
+ }
203
+ /**
204
+ * An in-memory {@link ResourceCacheSeam}: the seam's compare-on-set contract
205
+ * (README → Seams → the resource cache, pulpit#121). It keeps, per id, the
206
+ * highest version either call has given it: a `set` below it is a no-op, a
207
+ * `set` at or above it stores the resource when `serveable` admits it and
208
+ * evicts it otherwise, and a `delete` evicts and raises the version. Each
209
+ * call compares and writes in one step, as the real cache's conditional write
210
+ * must. In memory only; not for production.
211
+ *
212
+ * @since 0.1.0
213
+ */
214
+ export declare const makeInMemoryResourceCache: <Resource>(serveable: (resource: Resource) => boolean) => InMemoryResourceCache<Resource>;
215
+ /**
216
+ * An in-memory {@link DocumentStore}, plus the hooks a test reads it through.
217
+ * In memory only; not for production.
218
+ *
219
+ * @since 0.1.0
220
+ */
221
+ export interface InMemoryDocumentStore<Document extends Schema.Json> extends DocumentStore<Document, never, never> {
222
+ /** Every stored document by hash — what the never-rewritten assertions read. */
223
+ readonly stored: () => HashMap.HashMap<DocumentHash, Document>;
224
+ /**
225
+ * How many times `put` wrote a row: a real insert counts, a no-op `put` of a
226
+ * stored hash does not. The never-rewritten assertions read it, because
227
+ * `HashMap.set` with a structurally equal value is indistinguishable from no
228
+ * write in {@link InMemoryDocumentStore.stored}.
229
+ */
230
+ readonly writes: () => number;
231
+ /**
232
+ * Delete the documents at `hashes`: what {@link makeInMemoryPurgeStore}
233
+ * deletes an unreferenced document through, and nothing else may call
234
+ * (`DOC-001`).
235
+ */
236
+ readonly remove: (hashes: ReadonlyArray<DocumentHash>) => void;
237
+ }
238
+ /**
239
+ * An in-memory {@link DocumentStore}: the seam's contract (README → Seams →
240
+ * `DocumentStore`), hash contract included. It hashes exactly as a Postgres
241
+ * store does, SHA-256 over the UTF-8 bytes of the document's
242
+ * {@link canonicalText}, so its hashes are the ones the database generates
243
+ * (the kernel's suite pins it to hashes taken from Postgres). Like a store's
244
+ * primary key on the hash with `ON CONFLICT DO NOTHING`, it keeps the first
245
+ * document stored at a hash and never replaces it (`DOC-001`). It hashes with
246
+ * Web Crypto (`crypto.subtle`), which Node.js 22 and every browser provide.
247
+ * In memory only; not for production.
248
+ *
249
+ * @since 0.1.0
250
+ */
251
+ export declare const makeInMemoryDocumentStore: <Document extends Schema.Json>() => InMemoryDocumentStore<Document>;
252
+ /**
253
+ * An in-memory {@link EndpointView} whose nodes a test writes directly. In
254
+ * memory only; not for production.
255
+ *
256
+ * @since 0.1.0
257
+ */
258
+ export interface InMemoryNodes extends EndpointView<never, never> {
259
+ /** Store (or replace) a node's current-version row. */
260
+ readonly put: (row: LeafRow) => void;
261
+ }
262
+ /**
263
+ * An in-memory {@link EndpointView}: an edge end's current-version view
264
+ * (README → Seams → `EdgeStore`) as rows a test puts by hand, for an edge
265
+ * suite that doesn't need the endpoint leaf's version chain. Its `columns`
266
+ * are `id`, `version`, `action` and `columns`. An {@link InMemoryLeafStore} is an
267
+ * endpoint view too. In memory only; not for production.
268
+ *
269
+ * @since 0.1.0
270
+ */
271
+ export declare const makeInMemoryNodes: (columns: ReadonlyArray<string>) => InMemoryNodes;
272
+ /**
273
+ * An in-memory {@link EdgeStore}, plus the hooks a test reads and steers it
274
+ * through. In memory only; not for production.
275
+ *
276
+ * @since 0.1.0
277
+ */
278
+ export interface InMemoryEdgeStore extends EdgeStore<EdgeRow, InMemoryFollowing, never, never> {
279
+ /** Every stored edge, hidden ones included, in insertion order. */
280
+ readonly rows: () => ReadonlyArray<EdgeRow>;
281
+ /**
282
+ * Simulate a concurrent writer: the next insert finds its unique key just
283
+ * taken by `winner`, which is stored in its place (the racing writer's
284
+ * values, which share the key and may differ elsewhere).
285
+ */
286
+ readonly raceNextInsert: (winner: EdgeValues) => void;
287
+ }
288
+ /**
289
+ * Options for {@link makeInMemoryEdgeStore}.
290
+ *
291
+ * @since 0.1.0
292
+ */
293
+ export interface InMemoryEdgeStoreOptions {
294
+ /** The ends the list joins, by alias (the family declaration's `ends`). */
295
+ readonly ends: EdgeEnds<never, never>;
296
+ /** The unique key's columns; empty for a family with none. */
297
+ readonly uniqueKey: ReadonlyArray<string>;
298
+ /**
299
+ * The instant `published` / `notPublished` compare against, and the base of
300
+ * the minted `createdAt`s; the Effect `Clock`'s at each call when absent.
301
+ */
302
+ readonly now?: Date | undefined;
303
+ }
304
+ /**
305
+ * An in-memory {@link EdgeStore}: the seam's contract (README → Seams →
306
+ * `EdgeStore`) over one edge family's rows. It enforces what an edge table's
307
+ * schema enforces, the family's unique key (with `NULLS NOT DISTINCT`, as
308
+ * gardeneria's `page_slugs_unique_variant`), and nothing the operations are
309
+ * responsible for. `list`, `countAt`, `members` and `countBy` join each end's view the way the SQL
310
+ * store's INNER JOIN does: under the end's alias, dropping an edge whose end
311
+ * has no row. `list` orders by `createdAt` then `id` and reports the unpaged
312
+ * total as the SQL store's window column does (`0` for an empty page).
313
+ *
314
+ * Ids are minted `edge-0001`, `edge-0002`, …; the n-th insert is stamped
315
+ * `now + n` milliseconds, except that every third shares its predecessor's
316
+ * instant, so the `id` tie-break of the list order is exercised. In memory
317
+ * only; not for production.
318
+ *
319
+ * @since 0.1.0
320
+ */
321
+ export declare const makeInMemoryEdgeStore: (options: InMemoryEdgeStoreOptions) => InMemoryEdgeStore;
322
+ /**
323
+ * An in-memory {@link ProposalStore}, plus the hook a test reads it through.
324
+ * In memory only; not for production.
325
+ *
326
+ * @since 0.1.0
327
+ */
328
+ export interface InMemoryProposalStore extends ProposalStore<never, never> {
329
+ /** Every row, in insert order, as it now stands. */
330
+ readonly rows: () => ReadonlyArray<ProposalRow>;
331
+ }
332
+ /**
333
+ * An in-memory {@link ProposalStore}: the seam's contract (README → Seams →
334
+ * `ProposalStore`). A listing's `targetVersion` is the `chain`'s head for
335
+ * the target, and the `targetOwnerId` its `list` filters on is `leaves`'
336
+ * owner stamp (or `null`), as the SQL store's joins read them. `lock`
337
+ * locks nothing (one JavaScript thread has no concurrent decision to
338
+ * serialise). `mintId` mints sequential UUID-shaped ids. In memory only;
339
+ * not for production.
340
+ *
341
+ * @since 0.1.0
342
+ */
343
+ export declare const makeInMemoryProposalStore: (options: {
344
+ readonly chain: InMemoryChainStore;
345
+ readonly leaves: InMemoryLeafStore;
346
+ }) => InMemoryProposalStore;
347
+ /**
348
+ * An in-memory {@link ReportStore}, plus the hook a test reads it through.
349
+ * In memory only; not for production.
350
+ *
351
+ * @since 0.1.0
352
+ */
353
+ export interface InMemoryReportStore extends ReportStore<never, never> {
354
+ /** Every report, in filing order, as it now stands. */
355
+ readonly rows: () => ReadonlyArray<ReportRow>;
356
+ }
357
+ /**
358
+ * An in-memory {@link ReportStore}: the seam's contract (README → Seams →
359
+ * `ReportStore`). `list` evaluates the filter in memory, newest first with
360
+ * `id` descending on a tie; `lock` locks nothing (one JavaScript thread has
361
+ * no concurrent resolution to serialise); `mintId` mints sequential
362
+ * UUID-shaped ids. In memory only; not for production.
363
+ *
364
+ * @since 0.1.0
365
+ */
366
+ export declare const makeInMemoryReportStore: () => InMemoryReportStore;
367
+ declare const InMemoryBlobFailure_base: Schema.Class<InMemoryBlobFailure, Schema.TaggedStruct<"InMemoryBlobFailure", {
368
+ readonly key: Schema.String;
369
+ }>, import("effect/Cause").YieldableError & {
370
+ readonly brand: unique symbol;
371
+ }>;
372
+ /**
373
+ * The in-memory blob store's one failure: a simulated outage.
374
+ *
375
+ * @since 0.1.0
376
+ */
377
+ export declare class InMemoryBlobFailure extends InMemoryBlobFailure_base {
378
+ }
379
+ /**
380
+ * One object in an {@link InMemoryBlobStore}.
381
+ *
382
+ * @since 0.1.0
383
+ */
384
+ export interface InMemoryBlob {
385
+ readonly bytes: Uint8Array;
386
+ readonly head: BlobHead;
387
+ }
388
+ /**
389
+ * An in-memory {@link BlobStore}, plus the hooks a test reads and steers it
390
+ * through. In memory only; not for production.
391
+ *
392
+ * @since 0.1.0
393
+ */
394
+ export interface InMemoryBlobStore extends BlobStore<InMemoryBlobFailure, never> {
395
+ /** Every stored object by key. */
396
+ readonly objects: () => HashMap.HashMap<string, InMemoryBlob>;
397
+ /** Simulate an outage: the next `put` fails with {@link InMemoryBlobFailure}. */
398
+ readonly failNextPut: () => void;
399
+ }
400
+ /**
401
+ * An in-memory {@link BlobStore}: the seam's contract (README → Seams →
402
+ * `BlobStore`). `publicUrl` is `memory://<key>`; there is no signed upload
403
+ * URL. `failNextPut` simulates the byte write failing after the commit. In
404
+ * memory only; not for production.
405
+ *
406
+ * @since 0.1.0
407
+ */
408
+ export declare const makeInMemoryBlobStore: () => InMemoryBlobStore;
409
+ /**
410
+ * A publish-gated leaf's in-memory seams, over an in-memory chain and leaf
411
+ * store, plus the hook a test reads the log through. In memory only; not
412
+ * for production.
413
+ *
414
+ * @since 0.1.0
415
+ */
416
+ export interface InMemoryPublication {
417
+ /** The publication log. */
418
+ readonly facts: PublicationStore<never, never>;
419
+ /** The live-version view. */
420
+ readonly live: LiveView<LeafRow, InMemoryFollowing, never, never>;
421
+ /** The leaf's current-version view, with `draft` computed from the log. */
422
+ readonly leaves: LeafStore<LeafRow, InMemoryFollowing, never, never>;
423
+ /** Every fact, in record order. */
424
+ readonly recorded: () => ReadonlyArray<PublicationFact>;
425
+ }
426
+ /**
427
+ * The in-memory seams of a publish-gated leaf: the seam contracts (README →
428
+ * Seams → `PublicationStore`, `LiveView`). The effective fact is the last
429
+ * recorded one whose `at` has arrived at `now` (the Effect `Clock`'s when
430
+ * absent, so `TestClock` steers a schedule). A live row is the state at the
431
+ * effective fact's version, with that version as its `version`, the
432
+ * head's `action` and owner, `draft: false` and the fact's `at` as
433
+ * `publishedAt`; a head row's `draft`
434
+ * is whether the node has no live version. In memory only; not for
435
+ * production.
436
+ *
437
+ * @since 0.1.0
438
+ */
439
+ export declare const makeInMemoryPublication: (options: {
440
+ readonly chain: InMemoryChainStore;
441
+ readonly leaves: InMemoryLeafStore;
442
+ readonly now?: Date | undefined;
443
+ }) => InMemoryPublication;
444
+ /**
445
+ * An in-memory {@link WorkingCopyStore}, plus the hook a test reads it
446
+ * through. In memory only; not for production.
447
+ *
448
+ * @since 0.1.0
449
+ */
450
+ export interface InMemoryWorkingCopyStore extends WorkingCopyStore<never, never> {
451
+ /** Every copy, keyed `id:userId`. */
452
+ readonly copies: () => HashMap.HashMap<string, WorkingCopy>;
453
+ }
454
+ /**
455
+ * An in-memory {@link WorkingCopyStore}: the seam's contract (README →
456
+ * Seams → `WorkingCopyStore`), one copy per `(id, userId)`. In memory only;
457
+ * not for production.
458
+ *
459
+ * @since 0.1.0
460
+ */
461
+ export declare const makeInMemoryWorkingCopyStore: () => InMemoryWorkingCopyStore;
462
+ /**
463
+ * Options for {@link makeInMemoryPurgeStore}.
464
+ *
465
+ * @since 0.1.0
466
+ */
467
+ export interface InMemoryPurgeStoreOptions {
468
+ /**
469
+ * Every chain whose version rows may reference a document or bytes: the
470
+ * purged node's rows are erased from each, and a hash is unreferenced when
471
+ * no row left in any of them carries it.
472
+ */
473
+ readonly chains: ReadonlyArray<InMemoryChainStore>;
474
+ /** The documents an unreferenced hash is deleted from; omitted, none is deleted (or reported). */
475
+ readonly documents?: Pick<InMemoryDocumentStore<Schema.Json>, "remove"> | undefined;
476
+ /** The state columns that carry a `DocumentHash`; omitted, none do. */
477
+ readonly documentColumns?: ReadonlyArray<string> | undefined;
478
+ /** The state columns that carry a `ContentHash` (a file leaf's `contentHash`); omitted, none do. */
479
+ readonly blobColumns?: ReadonlyArray<string> | undefined;
480
+ /**
481
+ * What else `eraseNode` deletes for the node: the rows other fakes keep
482
+ * for it (an owner stamp through `InMemoryLeafStore.dropOwner`, working
483
+ * copies, edges, …), as a real store's `ON DELETE CASCADE` would.
484
+ */
485
+ readonly dependents?: ReadonlyArray<(id: string) => void> | undefined;
486
+ }
487
+ /**
488
+ * An in-memory {@link PurgeStore} over in-memory chains and a document
489
+ * store: the seam's contract (README → Seams → `PurgeStore`). `eraseNode`
490
+ * erases the node's rows from every chain and runs the `dependents`;
491
+ * a hash is referenced while any row left in any chain carries it in a
492
+ * declared column. In memory only; not for production.
493
+ *
494
+ * @since 0.1.0
495
+ */
496
+ export declare const makeInMemoryPurgeStore: (options: InMemoryPurgeStoreOptions) => PurgeStore<never, never>;
497
+ /**
498
+ * An in-memory {@link SearchBranch} over `rows`: each row the scope admits
499
+ * (evaluated at `now` over {@link InMemoryFollowing}) and `score` gives a
500
+ * score for the query is a hit, best score first, ties by id, at most
501
+ * `request.limit`. The reference semantics of the branch contract: it
502
+ * applies the scope it is handed in the same pass that ranks.
503
+ *
504
+ * @since 0.1.0
505
+ */
506
+ export declare const makeInMemorySearchBranch: (options: {
507
+ readonly name: string;
508
+ readonly weight?: number | undefined;
509
+ readonly rows: () => ReadonlyArray<FilterRow & {
510
+ readonly id: string;
511
+ }>;
512
+ /** The row's score for `query`, or none when it doesn't match. */
513
+ readonly score: (row: FilterRow, query: string) => Option.Option<number>;
514
+ readonly now: Date;
515
+ }) => SearchBranch<InMemoryFollowing, never, never>;
516
+ /**
517
+ * Wrap a {@link SearchBranch} so it fails {@link SearchScopeLeak} when it
518
+ * returns a hit whose row (looked up in `rows` by id) the scope it was
519
+ * handed does not admit, or that `rows` lacks: the test seam an
520
+ * application wraps around its own SQL branches to prove each applies the
521
+ * scope (`SEC-AUTH-ENUM-001`).
522
+ *
523
+ * @since 0.1.0
524
+ */
525
+ export declare const scopeCheckedBranch: <E, R>(branch: SearchBranch<InMemoryFollowing, E, R>, options: {
526
+ readonly rows: () => ReadonlyArray<FilterRow & {
527
+ readonly id: string;
528
+ }>;
529
+ readonly now: Date;
530
+ }) => SearchBranch<InMemoryFollowing, E | SearchScopeLeak, R>;
531
+ /**
532
+ * {@link makeInMemoryEmbeddingStore}'s fake: the seam plus test hooks.
533
+ *
534
+ * @since 0.1.0
535
+ */
536
+ export interface InMemoryEmbeddingStore extends EmbeddingStore<never, never> {
537
+ /** The queued hashes, in queue order. */
538
+ readonly queued: () => ReadonlyArray<DocumentHash>;
539
+ /** The written vectors, by hash. */
540
+ readonly vectors: () => HashMap.HashMap<DocumentHash, ReadonlyArray<number>>;
541
+ }
542
+ /**
543
+ * An in-memory {@link EmbeddingStore}: a queue in insertion order
544
+ * (`enqueue` is idempotent, and a hash with a vector is never queued again),
545
+ * `pending` the oldest `limit` queued, `write` storing each vector and
546
+ * dequeuing it, and `discard` dequeuing.
547
+ *
548
+ * @since 0.1.0
549
+ */
550
+ export declare const makeInMemoryEmbeddingStore: () => InMemoryEmbeddingStore;
551
+ /**
552
+ * One write {@link makeInMemoryWriteCountStore} has recorded.
553
+ *
554
+ * @since 0.1.0
555
+ */
556
+ export interface RecordedWrite {
557
+ readonly type: string;
558
+ readonly action: string;
559
+ readonly accountId: string;
560
+ readonly at: Date;
561
+ }
562
+ /**
563
+ * An in-memory atomic {@link BudgetCounter}, plus the hook a test reads it
564
+ * through. In memory only; not for production.
565
+ *
566
+ * @since 0.1.0
567
+ */
568
+ export interface InMemoryBudgetCounter extends BudgetCounter<never, never> {
569
+ /** Every spend it admitted, in order. */
570
+ readonly spends: () => ReadonlyArray<BudgetRequest>;
571
+ }
572
+ /**
573
+ * An in-memory atomic {@link BudgetCounter} (README → Seams →
574
+ * `BudgetCounter`): it counts the spends it admitted at or after the
575
+ * request's `since`, and records a spend only when it answers `true`. One
576
+ * JavaScript thread makes its check-and-record atomic. In memory only; not
577
+ * for production.
578
+ *
579
+ * @since 0.1.0
580
+ */
581
+ export declare const makeInMemoryBudgetCounter: () => InMemoryBudgetCounter;
582
+ /**
583
+ * An in-memory {@link WriteCountStore}, plus `record`, the row a write
584
+ * inserts. In memory only; not for production.
585
+ *
586
+ * @since 0.1.0
587
+ */
588
+ export interface InMemoryWriteCountStore extends WriteCountStore<never, never> {
589
+ /** Record one write, as the family's own row would. */
590
+ readonly record: (write: RecordedWrite) => void;
591
+ }
592
+ /**
593
+ * An in-memory {@link WriteCountStore} (README → Seams →
594
+ * `WriteCountStore`): `countSince` counts the recorded writes of the key at
595
+ * or after `since`. In memory only; not for production.
596
+ *
597
+ * @since 0.1.0
598
+ */
599
+ export declare const makeInMemoryWriteCountStore: () => InMemoryWriteCountStore;
600
+ /**
601
+ * {@link makeInMemoryReactionStore}'s fake: the seam plus a test hook.
602
+ *
603
+ * @since 0.1.0
604
+ */
605
+ export interface InMemoryReactionStore extends ReactionStore<never, never> {
606
+ /** Every stored reaction. */
607
+ readonly rows: () => ReadonlyArray<ReactionRow>;
608
+ }
609
+ /**
610
+ * An in-memory {@link ReactionStore}, keyed `(nodeId, accountId)` as the
611
+ * real table is: `upsert` replaces, `tally` groups by node and kind,
612
+ * `byAccount` is newest first (`nodeId` breaking a tie).
613
+ *
614
+ * @since 0.1.0
615
+ */
616
+ export declare const makeInMemoryReactionStore: () => InMemoryReactionStore;
617
+ /**
618
+ * {@link makeInMemoryVersionedKv}'s fake: the seam plus test hooks.
619
+ *
620
+ * @since 0.1.0
621
+ */
622
+ export interface InMemoryVersionedKv extends VersionedKv<never, never> {
623
+ /** The stored entry at `key` with its expiry instant (ms), expired or not. */
624
+ readonly peek: (key: string) => Option.Option<KvEntry & {
625
+ readonly expiresAt: number;
626
+ }>;
627
+ /** Overwrite `key` unconditionally (a corrupt or foreign value, for a decode test). */
628
+ readonly poke: (key: string, entry: KvEntry) => void;
629
+ }
630
+ /**
631
+ * An in-memory {@link VersionedKv}: `putIfNotBehind` compares and writes in
632
+ * one step, and an entry past its TTL reads as absent (the Effect `Clock`,
633
+ * so `TestClock` steers expiry). In memory only; not for production.
634
+ *
635
+ * @since 0.1.0
636
+ */
637
+ export declare const makeInMemoryVersionedKv: () => InMemoryVersionedKv;
638
+ /**
639
+ * {@link makeInMemoryCollectionStore}'s fake: the seam plus a test hook.
640
+ *
641
+ * @since 0.1.0
642
+ */
643
+ export interface InMemoryCollectionStore extends CollectionStore<never, never> {
644
+ /** Every parent's entries, as stored. */
645
+ readonly rows: () => HashMap.HashMap<string, ReadonlyArray<CollectionEntry>>;
646
+ }
647
+ /**
648
+ * An in-memory {@link CollectionStore}: each parent's entries, replaced
649
+ * whole, read back by position then member id. In memory only; not for
650
+ * production.
651
+ *
652
+ * @since 0.1.0
653
+ */
654
+ export declare const makeInMemoryCollectionStore: () => InMemoryCollectionStore;
655
+ /**
656
+ * {@link makeInMemorySocialStore}'s fake: the seam plus a test hook.
657
+ *
658
+ * @since 0.1.0
659
+ */
660
+ export interface InMemorySocialStore extends SocialStore<InMemoryFollowing, never, never> {
661
+ /** Every stored relationship. */
662
+ readonly rows: () => ReadonlyArray<Relationship>;
663
+ }
664
+ /**
665
+ * An in-memory {@link SocialStore}, keyed `(kind, from, to)` as the real
666
+ * table is; `followingOf` and `silencedBy` return {@link InMemoryFollowing}
667
+ * sets, where SQL's are subqueries. `addFollow` and `addBlock` each run as
668
+ * one synchronous step, so they are atomic against each other as the seam
669
+ * requires. In memory only; not for production.
670
+ *
671
+ * @since 0.1.0
672
+ */
673
+ export declare const makeInMemorySocialStore: () => InMemorySocialStore;
674
+ export {};
675
+ //# sourceMappingURL=testing.d.ts.map