@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.
Files changed (143) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +1271 -1
  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 +138 -4
  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
package/dist/index.js ADDED
@@ -0,0 +1,133 @@
1
+ /**
2
+ * `@thomasfosterau/pulpit` — the versioned-content kernel.
3
+ *
4
+ * Every module is exported as a namespace from the package root, following
5
+ * the convention of `effect` and `@thomasfosterau/effect-jsonapi`; each is
6
+ * also a subpath (`@thomasfosterau/pulpit/content`, …) for a consumer that
7
+ * wants one module without the barrel. The in-memory fakes are the one
8
+ * exception: they are the subpath `@thomasfosterau/pulpit/testing` only, so
9
+ * that production code never pulls them in through the barrel.
10
+ *
11
+ * @copyright Thomas Foster
12
+ * @module pulpit
13
+ */
14
+ /**
15
+ * The Content model fields: `Visibility`, the visibility / draft / timestamp /
16
+ * tombstone spreads, `ownerRef`, the base `content` resource.
17
+ */
18
+ export * as Content from "./content.js";
19
+ /**
20
+ * The registry flag vocabulary, `validateRegistry`, and the derived families /
21
+ * branches.
22
+ */
23
+ export * as Registry from "./registry.js";
24
+ /**
25
+ * The append-only node / version chain: create, update, restore, delete,
26
+ * getVersion, compare (with the pure `diffStates`), restoreVersion, history —
27
+ * over a store seam.
28
+ */
29
+ export * as VersionChain from "./version-chain.js";
30
+ /** The list-filter vocabulary the read predicates render into, and its in-memory evaluator. */
31
+ export * as Filter from "./filter.js";
32
+ /**
33
+ * The subtype operation factory: `LeafAccess`, the read scope and viewer
34
+ * predicates, the write gates, `defineSubtype` and its handlers seam.
35
+ */
36
+ export * as Subtype from "./subtype.js";
37
+ /**
38
+ * The publication axis, the moderation state, and the cache serveability
39
+ * policy (`isPubliclyServeable`, the `serveable` hook).
40
+ */
41
+ export * as Publication from "./publication.js";
42
+ /**
43
+ * The content-addressed document store seam (`DocumentStore`), the
44
+ * `DocumentHash` it is keyed by, and the `documentRef` field spread a version
45
+ * row carries the reference in.
46
+ */
47
+ export * as Documents from "./documents.js";
48
+ /**
49
+ * The edge operations factory: a relation between nodes as its own family
50
+ * (`defineEdgeOps` over an `EdgeStore` seam), readable only when every end
51
+ * is, with declared cardinality limits.
52
+ */
53
+ export * as Edges from "./edges.js";
54
+ /**
55
+ * Suggested edits: a contributor's patch against a node's version, accepted
56
+ * through the target's own update or rejected (`defineProposals` over a
57
+ * `ProposalStore` seam), with its two-tier stale / conflict classification.
58
+ */
59
+ export * as Proposals from "./proposals.js";
60
+ /**
61
+ * File and media leaves: the blob-store seam (`BlobStore`) keyed by the
62
+ * content hash, the file field spread, the commit-ordered byte write
63
+ * (`defineFileWrites`) and the usage index's edge ends.
64
+ */
65
+ export * as Blobs from "./blobs.js";
66
+ /**
67
+ * Routable content: a leaf's path claims tied to its version chain
68
+ * (`routable` over a `PathStore` seam), the aliases a path change leaves
69
+ * behind, and the gated resolution of a path.
70
+ */
71
+ export * as Paths from "./paths.js";
72
+ /**
73
+ * The moderation workflow: the transition rules as a before hook
74
+ * (`moderationHook`), the decision (`defineModeration`), the review-queue
75
+ * filter, threshold grading (`gradeScores`) and member reports
76
+ * (`defineReports` over a `ReportStore` seam).
77
+ */
78
+ export * as Moderation from "./moderation.js";
79
+ /**
80
+ * The working copy: an editor's autosave buffer outside the version chain
81
+ * (`WorkingCopyStore`), committed as one ordinary version
82
+ * (`defineWorkingCopy`).
83
+ */
84
+ export * as WorkingCopy from "./working-copy.js";
85
+ /**
86
+ * The comments family: threaded comments on any node as versioned owned
87
+ * leaves (`defineComments`), behind the target's read gate and the
88
+ * moderation gate.
89
+ */
90
+ export * as Comments from "./comments.js";
91
+ /**
92
+ * Purge: erasure, the one destructive path through a node's history. It
93
+ * hard-deletes a node's chain and the rows kept for it, then the documents
94
+ * and bytes nothing else references (`definePurge` over a `PurgeStore`).
95
+ */
96
+ export * as Purge from "./purge.js";
97
+ /**
98
+ * Hybrid search: reciprocal rank fusion over ranked branches, the reranker
99
+ * and branch seams every branch's scope runs through, and the embedding
100
+ * pipeline as seams (`fuseRankings`, `hybridSearch`, `embedPending`).
101
+ */
102
+ export * as Search from "./search.js";
103
+ /**
104
+ * Write budgets: a declared ceiling on an account's writes in a rolling
105
+ * window, spent inside the write as a before hook or by a plain-row family,
106
+ * over a `BudgetCounter` seam (`bindBudget`, `budgetHook`).
107
+ */
108
+ export * as Budgets from "./budgets.js";
109
+ /**
110
+ * Reactions: one reaction per account per node, a kind from the family's
111
+ * vocabulary, behind the target's read gate, with a grouped tally
112
+ * (`defineReactions` over a `ReactionStore`).
113
+ */
114
+ export * as Reactions from "./reactions.js";
115
+ /**
116
+ * The reference resource cache: `./subtype`'s `ResourceCacheSeam`, with a
117
+ * read-through `get`, over a minimal versioned key-value seam
118
+ * (`makeResourceCache` over a `VersionedKv`).
119
+ */
120
+ export * as Cache from "./cache.js";
121
+ /**
122
+ * Ordered collections: positioned members of a parent node, the order kept
123
+ * beside the chain or versioned with the parent (`defineCollection` over a
124
+ * `CollectionStore`).
125
+ */
126
+ export * as Collections from "./collections.js";
127
+ /**
128
+ * The social graph: follow, mute and block between accounts, the follower
129
+ * seam filled from it, and the opt-in read filter over silenced authors
130
+ * (`defineSocial` over a `SocialStore`).
131
+ */
132
+ export * as Social from "./social.js";
133
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH;;;GAGG;AACH,OAAO,KAAK,OAAO,MAAM,cAAc,CAAC;AAExC;;;GAGG;AACH,OAAO,KAAK,QAAQ,MAAM,eAAe,CAAC;AAE1C;;;;GAIG;AACH,OAAO,KAAK,YAAY,MAAM,oBAAoB,CAAC;AAEnD,+FAA+F;AAC/F,OAAO,KAAK,MAAM,MAAM,aAAa,CAAC;AAEtC;;;GAGG;AACH,OAAO,KAAK,OAAO,MAAM,cAAc,CAAC;AAExC;;;GAGG;AACH,OAAO,KAAK,WAAW,MAAM,kBAAkB,CAAC;AAEhD;;;;GAIG;AACH,OAAO,KAAK,SAAS,MAAM,gBAAgB,CAAC;AAE5C;;;;GAIG;AACH,OAAO,KAAK,KAAK,MAAM,YAAY,CAAC;AAEpC;;;;GAIG;AACH,OAAO,KAAK,SAAS,MAAM,gBAAgB,CAAC;AAE5C;;;;GAIG;AACH,OAAO,KAAK,KAAK,MAAM,YAAY,CAAC;AAEpC;;;;GAIG;AACH,OAAO,KAAK,KAAK,MAAM,YAAY,CAAC;AAEpC;;;;;GAKG;AACH,OAAO,KAAK,UAAU,MAAM,iBAAiB,CAAC;AAE9C;;;;GAIG;AACH,OAAO,KAAK,WAAW,MAAM,mBAAmB,CAAC;AAEjD;;;;GAIG;AACH,OAAO,KAAK,QAAQ,MAAM,eAAe,CAAC;AAE1C;;;;GAIG;AACH,OAAO,KAAK,KAAK,MAAM,YAAY,CAAC;AAEpC;;;;GAIG;AACH,OAAO,KAAK,MAAM,MAAM,aAAa,CAAC;AAEtC;;;;GAIG;AACH,OAAO,KAAK,OAAO,MAAM,cAAc,CAAC;AAExC;;;;GAIG;AACH,OAAO,KAAK,SAAS,MAAM,gBAAgB,CAAC;AAE5C;;;;GAIG;AACH,OAAO,KAAK,KAAK,MAAM,YAAY,CAAC;AAEpC;;;;GAIG;AACH,OAAO,KAAK,WAAW,MAAM,kBAAkB,CAAC;AAEhD;;;;GAIG;AACH,OAAO,KAAK,MAAM,MAAM,aAAa,CAAC"}
@@ -0,0 +1,8 @@
1
+ import type { ListFilter } from "../filter.ts";
2
+ /**
3
+ * Every column of `filter` rewritten by `rename`; the tree's shape and every
4
+ * comparand are kept. Exhaustive over the node kinds, so a new kind cannot
5
+ * slip through with its column unrenamed.
6
+ */
7
+ export declare const renameColumns: <Following>(filter: ListFilter<Following>, rename: (column: string) => string) => ListFilter<Following>;
8
+ //# sourceMappingURL=filter-columns.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"filter-columns.d.ts","sourceRoot":"","sources":["../../src/internal/filter-columns.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE/C;;;;GAIG;AACH,eAAO,MAAM,aAAa,GAAI,SAAS,UAC7B,UAAU,CAAC,SAAS,CAAC,UACrB,CAAC,MAAM,EAAE,MAAM,KAAK,MAAM,KACjC,UAAU,CAAC,SAAS,CA4BpB,CAAC"}
@@ -0,0 +1,43 @@
1
+ /**
2
+ * **A list filter's columns, renamed**: `./edges` qualifies a leaf's filter
3
+ * with an end's alias through it, so a store can render that filter against
4
+ * the view it joined under the alias.
5
+ *
6
+ * Internal: not a package entry point (absent from `package.json`
7
+ * `exports`) and excluded from the generated API docs (`docgen.json`).
8
+ *
9
+ * @internal
10
+ */
11
+ import * as Match from "effect/Match";
12
+ /**
13
+ * Every column of `filter` rewritten by `rename`; the tree's shape and every
14
+ * comparand are kept. Exhaustive over the node kinds, so a new kind cannot
15
+ * slip through with its column unrenamed.
16
+ */
17
+ export const renameColumns = (filter, rename) => Match.value(filter).pipe(Match.discriminatorsExhaustive("kind")({
18
+ always: (node) => node,
19
+ never: (node) => node,
20
+ eq: (node) => ({ ...node, column: rename(node.column) }),
21
+ ne: (node) => ({ ...node, column: rename(node.column) }),
22
+ in: (node) => ({ ...node, column: rename(node.column) }),
23
+ notIn: (node) => ({ ...node, column: rename(node.column) }),
24
+ gt: (node) => ({ ...node, column: rename(node.column) }),
25
+ gte: (node) => ({ ...node, column: rename(node.column) }),
26
+ lt: (node) => ({ ...node, column: rename(node.column) }),
27
+ lte: (node) => ({ ...node, column: rename(node.column) }),
28
+ anyOf: (node) => ({ ...node, column: rename(node.column) }),
29
+ noneOf: (node) => ({ ...node, column: rename(node.column) }),
30
+ isNull: (node) => ({ ...node, column: rename(node.column) }),
31
+ published: (node) => ({ ...node, column: rename(node.column) }),
32
+ notPublished: (node) => ({ ...node, column: rename(node.column) }),
33
+ locale: (node) => ({ ...node, column: rename(node.column) }),
34
+ and: (node) => ({
35
+ kind: "and",
36
+ filters: node.filters.map((part) => renameColumns(part, rename)),
37
+ }),
38
+ or: (node) => ({
39
+ kind: "or",
40
+ filters: node.filters.map((part) => renameColumns(part, rename)),
41
+ }),
42
+ }));
43
+ //# sourceMappingURL=filter-columns.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"filter-columns.js","sourceRoot":"","sources":["../../src/internal/filter-columns.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ import * as Order from "effect/Order";
2
+ import type { ColumnValue } from "../version-chain.ts";
3
+ /**
4
+ * Strings by code point, as SQL's `"C"` collation orders them over UTF-8.
5
+ * JavaScript's `<` compares UTF-16 code units instead, which puts an astral
6
+ * character (a surrogate pair, from U+D800) before U+E000–U+FFFF.
7
+ */
8
+ export declare const byCodePoint: Order.Order<string>;
9
+ /** One order key, structurally (`./subtype`'s `ListOrder` fits). */
10
+ interface OrderKey {
11
+ readonly column: string;
12
+ readonly direction: "asc" | "desc";
13
+ }
14
+ /** A row as the order reads it: its `id` and its columns by name. */
15
+ type SortRow = {
16
+ readonly id: string;
17
+ readonly [column: string]: ColumnValue;
18
+ };
19
+ /**
20
+ * The total order a list renders as `ORDER BY <key> <direction> NULLS LAST,
21
+ * …, id ASC`: each key in turn, a `NULL` (or absent) column after every
22
+ * value in either direction, then `id` ascending, so no two rows tie.
23
+ */
24
+ export declare const rowOrder: (keys: ReadonlyArray<OrderKey>) => Order.Order<SortRow>;
25
+ export {};
26
+ //# sourceMappingURL=list-order.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"list-order.d.ts","sourceRoot":"","sources":["../../src/internal/list-order.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,KAAK,MAAM,cAAc,CAAC;AAEtC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAEvD;;;;GAIG;AACH,eAAO,MAAM,WAAW,EAAE,KAAK,CAAC,KAAK,CAAC,MAAM,CAG3C,CAAC;AA8DF,oEAAoE;AACpE,UAAU,QAAQ;IAChB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,SAAS,EAAE,KAAK,GAAG,MAAM,CAAC;CACpC;AAED,qEAAqE;AACrE,KAAK,OAAO,GAAG;IAAE,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,GAAG,WAAW,CAAA;CAAE,CAAC;AAgB/E;;;;GAIG;AACH,eAAO,MAAM,QAAQ,SAAU,aAAa,CAAC,QAAQ,CAAC,KAAG,KAAK,CAAC,KAAK,CAAC,OAAO,CACvB,CAAC"}
@@ -0,0 +1,92 @@
1
+ /**
2
+ * **The kernel's value and row orders**: the code-point string order that
3
+ * `./filter`'s comparison arms share with SQL's `COLLATE "C"`, and the list
4
+ * order the in-memory `LeafStore` fake sorts by (pulpit#112), so the fake and
5
+ * the filter never compare a string two ways.
6
+ *
7
+ * Internal: not a package entry point (absent from `package.json`
8
+ * `exports`) and excluded from the generated API docs (`docgen.json`).
9
+ * Typed structurally, so it never imports `./subtype`.
10
+ *
11
+ * @internal
12
+ */
13
+ import * as Array from "effect/Array";
14
+ import * as Order from "effect/Order";
15
+ import * as Predicate from "effect/Predicate";
16
+ /**
17
+ * Strings by code point, as SQL's `"C"` collation orders them over UTF-8.
18
+ * JavaScript's `<` compares UTF-16 code units instead, which puts an astral
19
+ * character (a surrogate pair, from U+D800) before U+E000–U+FFFF.
20
+ */
21
+ export const byCodePoint = Order.mapInput(Order.Array(Order.Number), (text) => Array.map(Array.fromIterable(text), (char) => char.codePointAt(0) ?? 0));
22
+ /**
23
+ * Numbers as PostgreSQL's `float8` sorts them: `NaN` equal to itself and
24
+ * after every number, where Effect's `Order.Number` puts it first.
25
+ */
26
+ const byNumber = Order.make((self, that) => {
27
+ if (Number.isNaN(self))
28
+ return Number.isNaN(that) ? 0 : 1;
29
+ return Number.isNaN(that) ? -1 : Order.Number(self, that);
30
+ });
31
+ /**
32
+ * One kind of sort-key value: its guard and its order, the order lifted to
33
+ * every {@link ColumnValue} (two values the guard doesn't both admit tie).
34
+ */
35
+ const kind = (is, order) => ({
36
+ is,
37
+ order: Order.make((self, that) => (is(self) && is(that) ? order(self, that) : 0)),
38
+ });
39
+ /**
40
+ * The kinds of sort-key value, each sorted as PostgreSQL sorts its type: a
41
+ * boolean `false` first, a number numerically, a `bigint` by value, a
42
+ * string by code point, a `Date` by its instant (an invalid one as `NaN`).
43
+ */
44
+ const kinds = [
45
+ kind(Predicate.isBoolean, Order.Boolean),
46
+ kind(Predicate.isNumber, byNumber),
47
+ kind(Predicate.isBigInt, Order.BigInt),
48
+ kind(Predicate.isString, byCodePoint),
49
+ kind(Predicate.isDate, Order.mapInput(byNumber, (date) => date.getTime())),
50
+ ];
51
+ /**
52
+ * A value's place in {@link kinds}; a JSON object or array comes after
53
+ * them all. Two values of different kinds sort by it, which is outside the
54
+ * contract (a SQL column has one type), so it only keeps the fake's order
55
+ * total and deterministic.
56
+ */
57
+ const kindOf = (value) => {
58
+ const index = kinds.findIndex((each) => each.is(value));
59
+ return index === -1 ? kinds.length : index;
60
+ };
61
+ /**
62
+ * Two non-`NULL` sort-key values: by kind, then by that kind's order. Two
63
+ * JSON objects or arrays tie, leaving the order to the next key.
64
+ */
65
+ const byValue = Order.make((self, that) => {
66
+ const left = kindOf(self);
67
+ const right = kindOf(that);
68
+ if (left !== right)
69
+ return Order.Number(left, right);
70
+ const same = kinds[left];
71
+ return same === undefined ? 0 : same.order(self, that);
72
+ });
73
+ /** One key: its column's values in its direction, a `NULL` or absent column last in both. */
74
+ const byKey = (key) => {
75
+ const values = key.direction === "desc" ? Order.flip(byValue) : byValue;
76
+ return Order.make((self, that) => {
77
+ const left = self[key.column] ?? null;
78
+ const right = that[key.column] ?? null;
79
+ if (left === null)
80
+ return right === null ? 0 : 1;
81
+ return right === null ? -1 : values(left, right);
82
+ });
83
+ };
84
+ /** The closing key every list order ends with: `id` ascending, by code point. */
85
+ const byId = Order.mapInput(byCodePoint, (row) => row.id);
86
+ /**
87
+ * The total order a list renders as `ORDER BY <key> <direction> NULLS LAST,
88
+ * …, id ASC`: each key in turn, a `NULL` (or absent) column after every
89
+ * value in either direction, then `id` ascending, so no two rows tie.
90
+ */
91
+ export const rowOrder = (keys) => Order.combineAll([...Array.map(keys, byKey), byId]);
92
+ //# sourceMappingURL=list-order.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"list-order.js","sourceRoot":"","sources":["../../src/internal/list-order.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,93 @@
1
+ /**
2
+ * **The owned leaf's row reading**, shared by `./subtype` and `./edges` so
3
+ * the two cannot read the same view or row differently: the declared-column
4
+ * check (pulpit#79), the facts read off a row (pulpit#80), and the predicate
5
+ * over those facts, which `canViewOwned` delegates to (pulpit#85).
6
+ *
7
+ * Internal: not a package entry point (absent from `package.json`
8
+ * `exports`) and excluded from the generated API docs (`docgen.json`).
9
+ * Typed structurally, so it never imports `./subtype`.
10
+ *
11
+ * @internal
12
+ */
13
+ import * as Effect from "effect/Effect";
14
+ import * as Option from "effect/Option";
15
+ import { Visibility } from "../content.ts";
16
+ import type { ColumnValue } from "../version-chain.ts";
17
+ /** The owned access declaration's column names, structurally (`./subtype`'s owned `LeafAccess` fits). */
18
+ interface OwnedColumns {
19
+ readonly ownerColumn: string;
20
+ readonly visibilityColumn: string;
21
+ readonly draftColumn: Option.Option<string>;
22
+ readonly moderationColumn: Option.Option<string>;
23
+ }
24
+ /**
25
+ * The facts {@link canViewOwnedRow} reads off an owned row: `./subtype`'s
26
+ * `OwnedFacts`, except that the owner may be absent. A row whose owner
27
+ * column holds no string has no owner (pulpit#85).
28
+ */
29
+ interface OwnedRowFacts {
30
+ readonly ownerId: Option.Option<string>;
31
+ readonly visibility: Visibility;
32
+ readonly draft: boolean;
33
+ /** Under review or rejected: a moderated row whose state is not exactly `approved` (pulpit#154). */
34
+ readonly unapproved: boolean;
35
+ }
36
+ /** The parts of `./subtype`'s viewer, policy and follower seam the predicate reads, structurally. */
37
+ interface Account {
38
+ readonly id: string;
39
+ }
40
+ interface PrivateRung<A> {
41
+ readonly canViewPrivate: (account: A) => boolean;
42
+ }
43
+ interface IsFollower<R> {
44
+ readonly isFollower: (followerId: string, ownerId: string) => Effect.Effect<boolean, never, R>;
45
+ }
46
+ /**
47
+ * Whether the view carries every column an owned leaf declares (owner,
48
+ * visibility and, when declared, draft and moderation state). An owned leaf whose view lacks one
49
+ * reads no row: `ownedListFilter` lists nothing, and `defineSubtype`'s
50
+ * `get` / `getMany` and `./edges`' `canViewEndpoint` show nothing. Fail
51
+ * closed (`SEC-AUTH-ENUM-001`). Never throws.
52
+ *
53
+ * @internal
54
+ */
55
+ export declare const declaresColumns: (access: OwnedColumns, viewColumns: ReadonlySet<string>) => boolean;
56
+ /**
57
+ * Read an owned row's facts for `canViewOwned`, in `defineSubtype`'s
58
+ * `get` / `getMany` and `./edges`' `canViewEndpoint`. Each reading fails
59
+ * closed where `ownedListFilter` does (`SEC-AUTH-ENUM-001`): a visibility
60
+ * outside the enum (a dropped projection, a schema drift) reads as
61
+ * `private`, since the filter's `eq(visibility, "public")` arm lists only
62
+ * `public`; a draft column holding anything but an explicit `false` (a
63
+ * `NULL`, an absent key, a value outside the type) reads as a draft, since
64
+ * its `eq(draft, false)` arms list only `false`. A leaf without the draft
65
+ * axis reads `draft: false`. A moderation column holding anything but
66
+ * exactly `"approved"` reads as unapproved, since the filter's
67
+ * `eq(moderation, "approved")` arms list only `approved`; a leaf without the
68
+ * axis reads `unapproved: false`. An owner column holding anything but a string
69
+ * (a `NULL`, an absent key, a value outside the type) reads as no owner,
70
+ * never as its `String` form (pulpit#85; see pulpit#75): its `eq(owner, …)`
71
+ * and `in(owner, following)` arms match no `NULL`. Callers check
72
+ * {@link declaresColumns} first. Never throws.
73
+ *
74
+ * @internal
75
+ */
76
+ export declare const ownedFacts: (access: OwnedColumns, row: {
77
+ readonly [column: string]: ColumnValue;
78
+ }) => OwnedRowFacts;
79
+ /**
80
+ * The owned-material read predicate over {@link ownedFacts}: `canViewOwned`
81
+ * in `./subtype` is this with a present owner, and `defineSubtype`'s
82
+ * `get` / `getMany` and `./edges`' `canViewEndpoint` call it directly. A
83
+ * row with no owner skips the owner arm and the follower check, so no
84
+ * viewer's id matches it and the follower seam is never asked about it:
85
+ * it is visible only when it is `public`, not a draft and not unapproved, or to the
86
+ * view-private rung, as `ownedListFilter` lists it (`SEC-AUTH-ENUM-001`).
87
+ * Never fails.
88
+ *
89
+ * @internal
90
+ */
91
+ export declare const canViewOwnedRow: <A extends Account, R>(viewer: Option.Option<A>, policy: PrivateRung<A>, followers: IsFollower<R>, row: OwnedRowFacts) => Effect.Effect<boolean, never, R>;
92
+ export {};
93
+ //# sourceMappingURL=owned-columns.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"owned-columns.d.ts","sourceRoot":"","sources":["../../src/internal/owned-columns.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AAGxC,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAC3C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAEvD,yGAAyG;AACzG,UAAU,YAAY;IACpB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IAC5C,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;CAClD;AAED;;;;GAIG;AACH,UAAU,aAAa;IACrB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IACxC,QAAQ,CAAC,UAAU,EAAE,UAAU,CAAC;IAChC,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,oGAAoG;IACpG,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;CAC9B;AAED,qGAAqG;AACrG,UAAU,OAAO;IACf,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;CACrB;AACD,UAAU,WAAW,CAAC,CAAC;IACrB,QAAQ,CAAC,cAAc,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,OAAO,CAAC;CAClD;AACD,UAAU,UAAU,CAAC,CAAC;IACpB,QAAQ,CAAC,UAAU,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC;CAChG;AAID;;;;;;;;GAQG;AACH,eAAO,MAAM,eAAe,WAAY,YAAY,eAAe,WAAW,CAAC,MAAM,CAAC,KAAG,OAM3C,CAAC;AAE/C;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,UAAU,WACb,YAAY,OACf;IAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,GAAG,WAAW,CAAA;CAAE,KAC9C,aAcD,CAAC;AAEH;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,eAAe,GAAI,CAAC,SAAS,OAAO,EAAE,CAAC,UAC1C,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,UAChB,WAAW,CAAC,CAAC,CAAC,aACX,UAAU,CAAC,CAAC,CAAC,OACnB,aAAa,KACjB,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,KAAK,EAAE,CAAC,CAajC,CAAC"}
@@ -0,0 +1,98 @@
1
+ /**
2
+ * **The owned leaf's row reading**, shared by `./subtype` and `./edges` so
3
+ * the two cannot read the same view or row differently: the declared-column
4
+ * check (pulpit#79), the facts read off a row (pulpit#80), and the predicate
5
+ * over those facts, which `canViewOwned` delegates to (pulpit#85).
6
+ *
7
+ * Internal: not a package entry point (absent from `package.json`
8
+ * `exports`) and excluded from the generated API docs (`docgen.json`).
9
+ * Typed structurally, so it never imports `./subtype`.
10
+ *
11
+ * @internal
12
+ */
13
+ import * as Effect from "effect/Effect";
14
+ import * as Option from "effect/Option";
15
+ import * as Predicate from "effect/Predicate";
16
+ import * as Schema from "effect/Schema";
17
+ import { Visibility } from "../content.js";
18
+ const isVisibility = Schema.is(Visibility);
19
+ /**
20
+ * Whether the view carries every column an owned leaf declares (owner,
21
+ * visibility and, when declared, draft and moderation state). An owned leaf whose view lacks one
22
+ * reads no row: `ownedListFilter` lists nothing, and `defineSubtype`'s
23
+ * `get` / `getMany` and `./edges`' `canViewEndpoint` show nothing. Fail
24
+ * closed (`SEC-AUTH-ENUM-001`). Never throws.
25
+ *
26
+ * @internal
27
+ */
28
+ export const declaresColumns = (access, viewColumns) => [
29
+ access.ownerColumn,
30
+ access.visibilityColumn,
31
+ ...Option.toArray(access.draftColumn),
32
+ ...Option.toArray(access.moderationColumn),
33
+ ].every((column) => viewColumns.has(column));
34
+ /**
35
+ * Read an owned row's facts for `canViewOwned`, in `defineSubtype`'s
36
+ * `get` / `getMany` and `./edges`' `canViewEndpoint`. Each reading fails
37
+ * closed where `ownedListFilter` does (`SEC-AUTH-ENUM-001`): a visibility
38
+ * outside the enum (a dropped projection, a schema drift) reads as
39
+ * `private`, since the filter's `eq(visibility, "public")` arm lists only
40
+ * `public`; a draft column holding anything but an explicit `false` (a
41
+ * `NULL`, an absent key, a value outside the type) reads as a draft, since
42
+ * its `eq(draft, false)` arms list only `false`. A leaf without the draft
43
+ * axis reads `draft: false`. A moderation column holding anything but
44
+ * exactly `"approved"` reads as unapproved, since the filter's
45
+ * `eq(moderation, "approved")` arms list only `approved`; a leaf without the
46
+ * axis reads `unapproved: false`. An owner column holding anything but a string
47
+ * (a `NULL`, an absent key, a value outside the type) reads as no owner,
48
+ * never as its `String` form (pulpit#85; see pulpit#75): its `eq(owner, …)`
49
+ * and `in(owner, following)` arms match no `NULL`. Callers check
50
+ * {@link declaresColumns} first. Never throws.
51
+ *
52
+ * @internal
53
+ */
54
+ export const ownedFacts = (access, row) => ({
55
+ ownerId: Option.liftPredicate(row[access.ownerColumn], Predicate.isString),
56
+ visibility: Option.getOrElse(Option.liftPredicate(row[access.visibilityColumn], isVisibility), () => "private"),
57
+ draft: Option.match(access.draftColumn, {
58
+ onNone: () => false,
59
+ onSome: (column) => row[column] !== false,
60
+ }),
61
+ unapproved: Option.match(access.moderationColumn, {
62
+ onNone: () => false,
63
+ onSome: (column) => row[column] !== "approved",
64
+ }),
65
+ });
66
+ /**
67
+ * The owned-material read predicate over {@link ownedFacts}: `canViewOwned`
68
+ * in `./subtype` is this with a present owner, and `defineSubtype`'s
69
+ * `get` / `getMany` and `./edges`' `canViewEndpoint` call it directly. A
70
+ * row with no owner skips the owner arm and the follower check, so no
71
+ * viewer's id matches it and the follower seam is never asked about it:
72
+ * it is visible only when it is `public`, not a draft and not unapproved, or to the
73
+ * view-private rung, as `ownedListFilter` lists it (`SEC-AUTH-ENUM-001`).
74
+ * Never fails.
75
+ *
76
+ * @internal
77
+ */
78
+ export const canViewOwnedRow = (viewer, policy, followers, row) => {
79
+ if (!row.draft && !row.unapproved && row.visibility === "public")
80
+ return Effect.succeed(true);
81
+ if (Option.isNone(viewer))
82
+ return Effect.succeed(false);
83
+ const account = viewer.value;
84
+ if (Option.contains(row.ownerId, account.id))
85
+ return Effect.succeed(true);
86
+ if (policy.canViewPrivate(account))
87
+ return Effect.succeed(true);
88
+ // Not the owner, not privileged: a draft, or a row not approved, is hidden whatever its visibility.
89
+ if (row.draft || row.unapproved)
90
+ return Effect.succeed(false);
91
+ if (row.visibility === "private")
92
+ return Effect.succeed(false);
93
+ return Option.match(row.ownerId, {
94
+ onNone: () => Effect.succeed(false),
95
+ onSome: (ownerId) => followers.isFollower(account.id, ownerId),
96
+ });
97
+ };
98
+ //# sourceMappingURL=owned-columns.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"owned-columns.js","sourceRoot":"","sources":["../../src/internal/owned-columns.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,32 @@
1
+ /**
2
+ * The pieces `./proposals` and `./moderation`'s reports share: both are
3
+ * plain-row families (not nodes) with a sign-in gate, a write clock and a
4
+ * page query narrowed to one leaf type, an optional target and an
5
+ * optional status.
6
+ *
7
+ * @module pulpit/internal/plain-rows
8
+ */
9
+ import * as Effect from "effect/Effect";
10
+ import * as Option from "effect/Option";
11
+ import { type ListFilter } from "../filter.ts";
12
+ import { Unauthorized, type Viewer, type ViewerAccount } from "../subtype.ts";
13
+ /** The write clock: the current instant, as the `Date` a row stores. */
14
+ export declare const nowDate: Effect.Effect<Date>;
15
+ /** The signed-in account, or `Unauthorized`. */
16
+ export declare const signedIn: <R>(viewer: Effect.Effect<Viewer, never, R>) => Effect.Effect<ViewerAccount, Unauthorized, R>;
17
+ /**
18
+ * A list request's store query: the leaf's rows under the viewer's `scope`,
19
+ * narrowed by target and status when asked, the page clamped to
20
+ * `MAX_PAGE_LIMIT` (`SEC-INPUT-003`).
21
+ */
22
+ export declare const plainRowQuery: <Status extends string>(type: string, scope: ListFilter<never>, request: {
23
+ readonly targetId: Option.Option<string>;
24
+ readonly status: Option.Option<Status>;
25
+ readonly limit: number;
26
+ readonly offset: number;
27
+ }) => {
28
+ where: ListFilter<never>;
29
+ limit: number;
30
+ offset: number;
31
+ };
32
+ //# sourceMappingURL=plain-rows.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plain-rows.d.ts","sourceRoot":"","sources":["../../src/internal/plain-rows.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,EAAmB,KAAK,UAAU,EAAE,MAAM,cAAc,CAAC;AAChE,OAAO,EAAkB,YAAY,EAAE,KAAK,MAAM,EAAE,KAAK,aAAa,EAAE,MAAM,eAAe,CAAC;AAE9F,wEAAwE;AACxE,eAAO,MAAM,OAAO,EAAE,MAAM,CAAC,MAAM,CAAC,IAAI,CAEvC,CAAC;AAEF,gDAAgD;AAChD,eAAO,MAAM,QAAQ,GAAI,CAAC,UAChB,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC,KACtC,MAAM,CAAC,MAAM,CAAC,aAAa,EAAE,YAAY,EAAE,CAAC,CAC4C,CAAC;AAE5F;;;;GAIG;AACH,eAAO,MAAM,aAAa,GAAI,MAAM,SAAS,MAAM,QAC3C,MAAM,SACL,UAAU,CAAC,KAAK,CAAC,WACf;IACP,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IACzC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IACvC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;IAED,KAAK;IASL,KAAK;IACL,MAAM;CACN,CAAC"}
@@ -0,0 +1,32 @@
1
+ /**
2
+ * The pieces `./proposals` and `./moderation`'s reports share: both are
3
+ * plain-row families (not nodes) with a sign-in gate, a write clock and a
4
+ * page query narrowed to one leaf type, an optional target and an
5
+ * optional status.
6
+ *
7
+ * @module pulpit/internal/plain-rows
8
+ */
9
+ import * as Clock from "effect/Clock";
10
+ import * as DateTime from "effect/DateTime";
11
+ import * as Effect from "effect/Effect";
12
+ import * as Option from "effect/Option";
13
+ import { always, and, eq } from "../filter.js";
14
+ import { MAX_PAGE_LIMIT, Unauthorized } from "../subtype.js";
15
+ /** The write clock: the current instant, as the `Date` a row stores. */
16
+ export const nowDate = Effect.map(Clock.currentTimeMillis, (millis) => DateTime.toDate(DateTime.makeUnsafe(millis)));
17
+ /** The signed-in account, or `Unauthorized`. */
18
+ export const signedIn = (viewer) => Effect.flatMap(viewer, (account) => Effect.fromOption(account, () => new Unauthorized()));
19
+ /**
20
+ * A list request's store query: the leaf's rows under the viewer's `scope`,
21
+ * narrowed by target and status when asked, the page clamped to
22
+ * `MAX_PAGE_LIMIT` (`SEC-INPUT-003`).
23
+ */
24
+ export const plainRowQuery = (type, scope, request) => ({
25
+ where: and(eq("targetType", type), scope, Option.match(request.targetId, { onNone: () => always, onSome: (id) => eq("targetId", id) }), Option.match(request.status, {
26
+ onNone: () => always,
27
+ onSome: (status) => eq("status", status),
28
+ })),
29
+ limit: Math.max(1, Math.min(Math.trunc(request.limit), MAX_PAGE_LIMIT)),
30
+ offset: Math.max(0, Math.trunc(request.offset)),
31
+ });
32
+ //# sourceMappingURL=plain-rows.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plain-rows.js","sourceRoot":"","sources":["../../src/internal/plain-rows.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,KAAK,MAAM,cAAc,CAAC;AACtC,OAAO,KAAK,QAAQ,MAAM,iBAAiB,CAAC;AAC5C,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,EAAE,EAAmB,MAAM,cAAc,CAAC;AAChE,OAAO,EAAE,cAAc,EAAE,YAAY,EAAmC,MAAM,eAAe,CAAC;AAE9F,wEAAwE;AACxE,MAAM,CAAC,MAAM,OAAO,GAAwB,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,iBAAiB,EAAE,CAAC,MAAM,EAAE,EAAE,CACzF,QAAQ,CAAC,MAAM,CAAC,QAAQ,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAC7C,CAAC;AAEF,gDAAgD;AAChD,MAAM,CAAC,MAAM,QAAQ,GAAG,CACtB,MAAuC,EACQ,EAAE,CACjD,MAAM,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,MAAM,CAAC,UAAU,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,IAAI,YAAY,EAAE,CAAC,CAAC,CAAC;AAE5F;;;;GAIG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,CAC3B,IAAY,EACZ,KAAwB,EACxB,OAKC,EACD,EAAE,CAAC,CAAC;IACJ,KAAK,EAAE,GAAG,CACR,EAAE,CAAC,YAAY,EAAE,IAAI,CAAC,EACtB,KAAK,EACL,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,UAAU,EAAE,EAAE,CAAC,EAAE,CAAC,EAC5F,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,EAAE;QAC3B,MAAM,EAAE,GAAG,EAAE,CAAC,MAAM;QACpB,MAAM,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,CAAC;KACzC,CAAC,CACH;IACD,KAAK,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,cAAc,CAAC,CAAC;IACvE,MAAM,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;CAChD,CAAC,CAAC"}