@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
@@ -0,0 +1,80 @@
1
+ /**
2
+ * **The reference leaf's privacy arm** (`PUB-009`), shared by `./subtype`
3
+ * and `./edges` so a reference row is read one way: as a list filter
4
+ * ({@link referenceListFilter}, behind `referencePrivacyFilter`, the subtype
5
+ * `list` and `./edges`' `endpointFilter`) and as a row predicate
6
+ * ({@link canViewReferenceRow}, behind `defineSubtype`'s `visibleRow` and
7
+ * `./edges`' `canViewEndpoint`). Before pulpit#92 each module spelled the
8
+ * predicate for itself.
9
+ *
10
+ * The row's facts are read by `ownedFacts`, so an owner-like value that is
11
+ * not a string reads as no owner and never reaches the follower seam
12
+ * (pulpit#85, pulpit#100).
13
+ *
14
+ * Internal: not a package entry point (absent from `package.json`
15
+ * `exports`) and excluded from the generated API docs (`docgen.json`).
16
+ * Typed structurally, so it never imports `./subtype`.
17
+ *
18
+ * @internal
19
+ */
20
+ import * as Effect from "effect/Effect";
21
+ import * as Option from "effect/Option";
22
+ import { type ListFilter } from "../filter.ts";
23
+ import type { ColumnValue } from "../version-chain.ts";
24
+ /** The reference access declaration, structurally (`./subtype`'s reference `LeafAccess` fits). */
25
+ interface ReferenceColumns {
26
+ readonly followersOfColumn?: string | undefined;
27
+ }
28
+ /** The parts of `./subtype`'s viewer, policy, follower seam and read scope the arm reads, structurally. */
29
+ interface Account {
30
+ readonly id: string;
31
+ }
32
+ interface PrivateRung<A> {
33
+ readonly canViewPrivate: (account: A) => boolean;
34
+ }
35
+ interface IsFollower<R> {
36
+ readonly isFollower: (followerId: string, ownerId: string) => Effect.Effect<boolean, never, R>;
37
+ }
38
+ type Scope<Following> = {
39
+ readonly kind: "all";
40
+ } | {
41
+ readonly kind: "public";
42
+ } | {
43
+ readonly kind: "member";
44
+ readonly userId: string;
45
+ readonly following: Following;
46
+ };
47
+ /**
48
+ * The reference privacy arm as a list filter: `always` over a view without a
49
+ * `visibility` column or for the view-private rung (`scope.kind === "all"`);
50
+ * otherwise `visibility = 'public'`, and for a signed-in viewer also
51
+ * `visibility = 'followers' AND (<followersOfColumn> = <viewer> OR
52
+ * <followersOfColumn> IN (<following>))` when the leaf declares that column
53
+ * and the view carries it (pulpit#92): the account the column names and its
54
+ * followers read its `followers` rows. Its `private` rows stay the
55
+ * view-private rung's alone. Never throws.
56
+ *
57
+ * @internal
58
+ */
59
+ export declare const referenceListFilter: <Following>(access: ReferenceColumns, viewColumns: ReadonlySet<string>, scope: Scope<Following>) => ListFilter<Following>;
60
+ /**
61
+ * The reference privacy arm as a row predicate, the same facts as
62
+ * {@link referenceListFilter}: a view without a `visibility` column shows
63
+ * the row; otherwise it shows when its visibility is exactly `public`, to
64
+ * the view-private rung, or, when it is exactly `followers` and the leaf
65
+ * declares a followers column the view carries, to the signed-in account
66
+ * that column names and to its followers, as the follower seam answers
67
+ * (pulpit#92). That account's `private` rows stay the view-private rung's
68
+ * alone. A column value that is not a string names no account, so it never
69
+ * matches the viewer and the seam is never asked about it (pulpit#85,
70
+ * pulpit#100). Anything else is hidden, which the
71
+ * caller answers as `NodeNotFound` (`SEC-AUTH-ENUM-001`). The soft-delete
72
+ * axis is the caller's. Never fails.
73
+ *
74
+ * @internal
75
+ */
76
+ export declare const canViewReferenceRow: <A extends Account, R>(access: ReferenceColumns, viewColumns: ReadonlySet<string>, viewer: Option.Option<A>, policy: PrivateRung<A>, followers: IsFollower<R>, row: {
77
+ readonly [column: string]: ColumnValue;
78
+ }) => Effect.Effect<boolean, never, R>;
79
+ export {};
80
+ //# sourceMappingURL=reference-columns.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reference-columns.d.ts","sourceRoot":"","sources":["../../src/internal/reference-columns.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AACH,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AAExC,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,EAA6B,KAAK,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAGvD,kGAAkG;AAClG,UAAU,gBAAgB;IAExB,QAAQ,CAAC,iBAAiB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACjD;AAED,2GAA2G;AAC3G,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;AACD,KAAK,KAAK,CAAC,SAAS,IAChB;IAAE,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAA;CAAE,GACxB;IAAE,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAA;CAAE,GAC3B;IAAE,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAA;CAAE,CAAC;AAkBxF;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,mBAAmB,GAAI,SAAS,UACnC,gBAAgB,eACX,WAAW,CAAC,MAAM,CAAC,SACzB,KAAK,CAAC,SAAS,CAAC,KACtB,UAAU,CAAC,SAAS,CAqBtB,CAAC;AAEF;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,mBAAmB,GAAI,CAAC,SAAS,OAAO,EAAE,CAAC,UAC9C,gBAAgB,eACX,WAAW,CAAC,MAAM,CAAC,UACxB,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,UAChB,WAAW,CAAC,CAAC,CAAC,aACX,UAAU,CAAC,CAAC,CAAC,OACnB;IAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,GAAG,WAAW,CAAA;CAAE,KAC9C,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,KAAK,EAAE,CAAC,CA8BjC,CAAC"}
@@ -0,0 +1,102 @@
1
+ /**
2
+ * **The reference leaf's privacy arm** (`PUB-009`), shared by `./subtype`
3
+ * and `./edges` so a reference row is read one way: as a list filter
4
+ * ({@link referenceListFilter}, behind `referencePrivacyFilter`, the subtype
5
+ * `list` and `./edges`' `endpointFilter`) and as a row predicate
6
+ * ({@link canViewReferenceRow}, behind `defineSubtype`'s `visibleRow` and
7
+ * `./edges`' `canViewEndpoint`). Before pulpit#92 each module spelled the
8
+ * predicate for itself.
9
+ *
10
+ * The row's facts are read by `ownedFacts`, so an owner-like value that is
11
+ * not a string reads as no owner and never reaches the follower seam
12
+ * (pulpit#85, pulpit#100).
13
+ *
14
+ * Internal: not a package entry point (absent from `package.json`
15
+ * `exports`) and excluded from the generated API docs (`docgen.json`).
16
+ * Typed structurally, so it never imports `./subtype`.
17
+ *
18
+ * @internal
19
+ */
20
+ import * as Effect from "effect/Effect";
21
+ import * as Match from "effect/Match";
22
+ import * as Option from "effect/Option";
23
+ import { always, and, eq, isIn, or } from "../filter.js";
24
+ import { ownedFacts } from "./owned-columns.js";
25
+ /** The reference axis's one column; a view without it rides no privacy axis. */
26
+ const VISIBILITY = "visibility";
27
+ /**
28
+ * The column whose followers may read a `followers` row, when the leaf
29
+ * declares one and the view carries it. A declared column the view lacks
30
+ * reads as none: the followers arm then matches nothing (fail closed).
31
+ */
32
+ const followersColumn = (access, viewColumns) => Option.filter(Option.fromUndefinedOr(access.followersOfColumn), (column) => viewColumns.has(column));
33
+ /**
34
+ * The reference privacy arm as a list filter: `always` over a view without a
35
+ * `visibility` column or for the view-private rung (`scope.kind === "all"`);
36
+ * otherwise `visibility = 'public'`, and for a signed-in viewer also
37
+ * `visibility = 'followers' AND (<followersOfColumn> = <viewer> OR
38
+ * <followersOfColumn> IN (<following>))` when the leaf declares that column
39
+ * and the view carries it (pulpit#92): the account the column names and its
40
+ * followers read its `followers` rows. Its `private` rows stay the
41
+ * view-private rung's alone. Never throws.
42
+ *
43
+ * @internal
44
+ */
45
+ export const referenceListFilter = (access, viewColumns, scope) => {
46
+ if (!viewColumns.has(VISIBILITY))
47
+ return always;
48
+ const column = followersColumn(access, viewColumns);
49
+ return Match.value(scope).pipe(Match.discriminatorsExhaustive("kind")({
50
+ all: () => always,
51
+ public: () => eq(VISIBILITY, "public"),
52
+ member: (member) => Option.match(column, {
53
+ onNone: () => eq(VISIBILITY, "public"),
54
+ onSome: (followed) => or(eq(VISIBILITY, "public"), and(eq(VISIBILITY, "followers"), or(eq(followed, member.userId), isIn(followed, member.following)))),
55
+ }),
56
+ }));
57
+ };
58
+ /**
59
+ * The reference privacy arm as a row predicate, the same facts as
60
+ * {@link referenceListFilter}: a view without a `visibility` column shows
61
+ * the row; otherwise it shows when its visibility is exactly `public`, to
62
+ * the view-private rung, or, when it is exactly `followers` and the leaf
63
+ * declares a followers column the view carries, to the signed-in account
64
+ * that column names and to its followers, as the follower seam answers
65
+ * (pulpit#92). That account's `private` rows stay the view-private rung's
66
+ * alone. A column value that is not a string names no account, so it never
67
+ * matches the viewer and the seam is never asked about it (pulpit#85,
68
+ * pulpit#100). Anything else is hidden, which the
69
+ * caller answers as `NodeNotFound` (`SEC-AUTH-ENUM-001`). The soft-delete
70
+ * axis is the caller's. Never fails.
71
+ *
72
+ * @internal
73
+ */
74
+ export const canViewReferenceRow = (access, viewColumns, viewer, policy, followers, row) => {
75
+ if (!viewColumns.has(VISIBILITY))
76
+ return Effect.succeed(true);
77
+ const visibility = row[VISIBILITY];
78
+ if (visibility === "public")
79
+ return Effect.succeed(true);
80
+ if (Option.isNone(viewer))
81
+ return Effect.succeed(false);
82
+ const account = viewer.value;
83
+ if (policy.canViewPrivate(account))
84
+ return Effect.succeed(true);
85
+ if (visibility !== "followers")
86
+ return Effect.succeed(false);
87
+ return Option.match(followersColumn(access, viewColumns), {
88
+ onNone: () => Effect.succeed(false),
89
+ onSome: (column) => Option.match(ownedFacts({
90
+ ownerColumn: column,
91
+ visibilityColumn: VISIBILITY,
92
+ draftColumn: Option.none(),
93
+ moderationColumn: Option.none(),
94
+ }, row).ownerId, {
95
+ onNone: () => Effect.succeed(false),
96
+ onSome: (ownerId) => ownerId === account.id
97
+ ? Effect.succeed(true)
98
+ : followers.isFollower(account.id, ownerId),
99
+ }),
100
+ });
101
+ };
102
+ //# sourceMappingURL=reference-columns.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reference-columns.js","sourceRoot":"","sources":["../../src/internal/reference-columns.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AACH,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,KAAK,KAAK,MAAM,cAAc,CAAC;AACtC,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,EAAmB,MAAM,cAAc,CAAC;AAE1E,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAuBhD,gFAAgF;AAChF,MAAM,UAAU,GAAG,YAAY,CAAC;AAEhC;;;;GAIG;AACH,MAAM,eAAe,GAAG,CACtB,MAAwB,EACxB,WAAgC,EACT,EAAE,CACzB,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,eAAe,CAAC,MAAM,CAAC,iBAAiB,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE,CACzE,WAAW,CAAC,GAAG,CAAC,MAAM,CAAC,CACxB,CAAC;AAEJ;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,CACjC,MAAwB,EACxB,WAAgC,EAChC,KAAuB,EACA,EAAE;IACzB,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,UAAU,CAAC;QAAE,OAAO,MAAM,CAAC;IAChD,MAAM,MAAM,GAAG,eAAe,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IACpD,OAAO,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,IAAI,CAC5B,KAAK,CAAC,wBAAwB,CAAC,MAAM,CAAC,CAAC;QACrC,GAAG,EAAE,GAA0B,EAAE,CAAC,MAAM;QACxC,MAAM,EAAE,GAA0B,EAAE,CAAC,EAAE,CAAC,UAAU,EAAE,QAAQ,CAAC;QAC7D,MAAM,EAAE,CAAC,MAAM,EAAyB,EAAE,CACxC,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE;YACnB,MAAM,EAAE,GAA0B,EAAE,CAAC,EAAE,CAAC,UAAU,EAAE,QAAQ,CAAC;YAC7D,MAAM,EAAE,CAAC,QAAQ,EAAE,EAAE,CACnB,EAAE,CACA,EAAE,CAAC,UAAU,EAAE,QAAQ,CAAC,EACxB,GAAG,CACD,EAAE,CAAC,UAAU,EAAE,WAAW,CAAC,EAC3B,EAAE,CAAY,EAAE,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,CAAC,EAAE,IAAI,CAAC,QAAQ,EAAE,MAAM,CAAC,SAAS,CAAC,CAAC,CAC7E,CACF;SACJ,CAAC;KACL,CAAC,CACH,CAAC;AACJ,CAAC,CAAC;AAEF;;;;;;;;;;;;;;;GAeG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,CACjC,MAAwB,EACxB,WAAgC,EAChC,MAAwB,EACxB,MAAsB,EACtB,SAAwB,EACxB,GAA+C,EACb,EAAE;IACpC,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,UAAU,CAAC;QAAE,OAAO,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9D,MAAM,UAAU,GAAG,GAAG,CAAC,UAAU,CAAC,CAAC;IACnC,IAAI,UAAU,KAAK,QAAQ;QAAE,OAAO,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IACzD,IAAI,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC;QAAE,OAAO,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IACxD,MAAM,OAAO,GAAG,MAAM,CAAC,KAAK,CAAC;IAC7B,IAAI,MAAM,CAAC,cAAc,CAAC,OAAO,CAAC;QAAE,OAAO,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IAChE,IAAI,UAAU,KAAK,WAAW;QAAE,OAAO,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IAC7D,OAAO,MAAM,CAAC,KAAK,CAAC,eAAe,CAAC,MAAM,EAAE,WAAW,CAAC,EAAE;QACxD,MAAM,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC;QACnC,MAAM,EAAE,CAAC,MAAM,EAAE,EAAE,CACjB,MAAM,CAAC,KAAK,CACV,UAAU,CACR;YACE,WAAW,EAAE,MAAM;YACnB,gBAAgB,EAAE,UAAU;YAC5B,WAAW,EAAE,MAAM,CAAC,IAAI,EAAE;YAC1B,gBAAgB,EAAE,MAAM,CAAC,IAAI,EAAE;SAChC,EACD,GAAG,CACJ,CAAC,OAAO,EACT;YACE,MAAM,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC;YACnC,MAAM,EAAE,CAAC,OAAO,EAAE,EAAE,CAClB,OAAO,KAAK,OAAO,CAAC,EAAE;gBACpB,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC;gBACtB,CAAC,CAAC,SAAS,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,EAAE,OAAO,CAAC;SAChD,CACF;KACJ,CAAC,CAAC;AACL,CAAC,CAAC"}
@@ -0,0 +1,392 @@
1
+ /**
2
+ * **The moderation workflow** — the rules that move a leaf's row between
3
+ * moderation states, the threshold grading of a classifier's scores, the
4
+ * review-queue filter, and member reports.
5
+ *
6
+ * The behavioural spec is gardeneria's comment moderation
7
+ * (`apps/api/src/operations/comments.ts`' `moderateCommentE`), its moderation
8
+ * plane (`apps/api/src/operations/moderation.ts`) and its threshold grader
9
+ * (`packages/jobs/src/moderation/thresholds.ts`), with their tests; a
10
+ * `#NNN` here names a gardeneria issue.
11
+ *
12
+ * ## The model (owner decisions on pulpit#51)
13
+ *
14
+ * The kernel owns the rules; the transport (a queue, the classifier run, the
15
+ * API-call ledger, moderator notifications) stays the application's.
16
+ *
17
+ * - **The state lives on the version row**, in the leaf's
18
+ * `moderationState` column ({@link ModerationState}): a decision is an
19
+ * appended version, so history shows who decided what, and when.
20
+ * - **Transitions.** Every new row starts `pending`. Only the moderate rung
21
+ * changes the state: `pending → approved | rejected`, re-moderation
22
+ * between `approved` and `rejected`, and re-review back to `pending`
23
+ * ({@link isModerationTransition}). A write by anyone else that changes the
24
+ * state fails `WriteRejected`.
25
+ * - **Re-review triggers are the leaf's**: an edit that changes one of its
26
+ * declared `reReviewOn` attributes sends an `approved` row back to
27
+ * `pending` ({@link ModerationDeclaration}).
28
+ * - **Owners withdraw their own content** by deleting it: the owned write
29
+ * gate admits its owner in every moderation state, and a tombstone needs
30
+ * no decision.
31
+ *
32
+ * {@link moderationHook} enforces the rules as a before hook (`./subtype`'s
33
+ * lifecycle seam), so every write path honours them, and
34
+ * {@link defineModeration}'s `moderate` is the decision itself.
35
+ *
36
+ * ## Grading and the queue
37
+ *
38
+ * {@link gradeScores} turns a classifier's per-category scores (shaped like
39
+ * effect-ai-ext's `ModerationModel` output; pulpit doesn't import it) into a
40
+ * verdict against the application's thresholds. Classification itself runs
41
+ * from an application's after hook. The review queue is the leaf's pending
42
+ * rows ({@link reviewQueueFilter}) plus the open member reports
43
+ * ({@link defineReports}), both the moderate rung's alone.
44
+ *
45
+ * @copyright Thomas Foster
46
+ * @module pulpit/moderation
47
+ * @since 0.1.0
48
+ */
49
+ import * as Effect from "effect/Effect";
50
+ import * as Option from "effect/Option";
51
+ import * as Result from "effect/Result";
52
+ import * as Schema from "effect/Schema";
53
+ import type { BoundBudget } from "./budgets.ts";
54
+ import { type ListFilter } from "./filter.ts";
55
+ import { ModerationState } from "./publication.ts";
56
+ import { Forbidden, Unauthorized, WriteRejected, type AccessPolicy, type BeforeWriteEvent, type BeforeWriteHook, type Page, type SubtypeOps, type Viewer } from "./subtype.ts";
57
+ import type { ChainPatch, ChainWrite, NodeNotFound, Precondition, StaleVersion, VersionConflict } from "./version-chain.ts";
58
+ /**
59
+ * Whether a row may move from `from` (none for a row being created) to
60
+ * `to`: a new row only to `pending`; an existing one to any other state
61
+ * (decision, re-moderation, re-review). Staying put is no transition.
62
+ *
63
+ * @example
64
+ * ```ts
65
+ * import { strictEqual } from "node:assert";
66
+ * import * as Option from "effect/Option";
67
+ * import { isModerationTransition } from "@thomasfosterau/pulpit/moderation";
68
+ *
69
+ * strictEqual(isModerationTransition(Option.none(), "pending"), true);
70
+ * strictEqual(isModerationTransition(Option.none(), "approved"), false);
71
+ * strictEqual(isModerationTransition(Option.some("pending"), "approved"), true);
72
+ * strictEqual(isModerationTransition(Option.some("approved"), "approved"), false);
73
+ * ```
74
+ *
75
+ * @since 0.1.0
76
+ */
77
+ export declare const isModerationTransition: (from: Option.Option<ModerationState>, to: ModerationState) => boolean;
78
+ /**
79
+ * A leaf's moderation columns and its re-review triggers.
80
+ *
81
+ * @since 0.1.0
82
+ */
83
+ export interface ModerationDeclaration {
84
+ /** The state column ({@link ModerationState}); conventionally `moderationState`. */
85
+ readonly column: string;
86
+ /** The column a decision stamps with its instant, when the leaf has one. */
87
+ readonly moderatedAtColumn: Option.Option<string>;
88
+ /** The column a decision stamps with the deciding account's id, when the leaf has one. */
89
+ readonly moderatedByColumn: Option.Option<string>;
90
+ /**
91
+ * The attributes whose change sends an `approved` row back to `pending`
92
+ * (a comment's `body`, an image's file). Empty, an approval survives
93
+ * every edit.
94
+ */
95
+ readonly reReviewOn: ReadonlyArray<string>;
96
+ }
97
+ /**
98
+ * The declaration with the conventional column names.
99
+ *
100
+ * @since 0.1.0
101
+ */
102
+ export declare const moderatedColumns: (reReviewOn: ReadonlyArray<string>) => ModerationDeclaration;
103
+ /**
104
+ * The moderation rules applied to one write: the patch the write should
105
+ * apply, or the reason it is refused. Pure, so every case is testable
106
+ * without a chain.
107
+ *
108
+ * - A **create** stores `pending`; a patch asking for another state is refused.
109
+ * - A **delete** is untouched (withdrawal needs no decision).
110
+ * - A **`restoreVersion`** by anyone but the moderate rung carries the
111
+ * prior row's moderation columns over the restored version's, so an old
112
+ * approval never comes back with old content.
113
+ * - A patch that **changes the state** is a decision: refused below the
114
+ * moderate rung or outside {@link isModerationTransition}, and otherwise
115
+ * stamped (`moderatedAtColumn`, `moderatedByColumn`). A patch that sets
116
+ * the state it already has changes nothing.
117
+ * - Otherwise, an edit that changes a `reReviewOn` attribute of an
118
+ * `approved` row sends it back to `pending`.
119
+ *
120
+ * @since 0.1.0
121
+ */
122
+ export declare const moderatePatch: (event: BeforeWriteEvent, declaration: ModerationDeclaration, policy: AccessPolicy, now: Date) => Result.Result<ChainPatch, string>;
123
+ /**
124
+ * The moderation rules ({@link moderatePatch}) as a before hook for the
125
+ * leaf's `LifecycleHooks`: every write applies them, and a refusal
126
+ * fails the write with `WriteRejected`, appending nothing. Compose it first,
127
+ * so later hooks see the state it settled.
128
+ *
129
+ * @since 0.1.0
130
+ */
131
+ export declare const moderationHook: <E = never, R = never>(declaration: ModerationDeclaration, policy: AccessPolicy) => BeforeWriteHook<E, R>;
132
+ /**
133
+ * The operations {@link defineModeration} produces.
134
+ *
135
+ * @since 0.1.0
136
+ */
137
+ export interface ModerationOps<E, R> {
138
+ /**
139
+ * Record a decision: append the state through the target's own `update`,
140
+ * which runs the {@link moderationHook} (stamping it). Fails
141
+ * `Unauthorized` when anonymous and `Forbidden` below the moderate rung,
142
+ * before anything is read, then as the target's `update` does
143
+ * (`NodeNotFound` for a missing or tombstoned row, `StaleVersion`, and
144
+ * `WriteRejected` from its hooks).
145
+ */
146
+ readonly moderate: (id: string, decision: ModerationState, precondition?: Precondition) => Effect.Effect<ChainWrite, Unauthorized | Forbidden | NodeNotFound | StaleVersion | VersionConflict | WriteRejected | E, R>;
147
+ }
148
+ /**
149
+ * Bind a leaf's moderation decision. The leaf's hooks must include
150
+ * {@link moderationHook} with the same declaration: it is what admits the
151
+ * decision and stamps it.
152
+ *
153
+ * @since 0.1.0
154
+ */
155
+ export declare const defineModeration: <Resource, E, R>(definition: {
156
+ readonly target: SubtypeOps<Resource, E, R>;
157
+ readonly declaration: ModerationDeclaration;
158
+ readonly viewer: Effect.Effect<Viewer, never, R>;
159
+ readonly policy: AccessPolicy;
160
+ }) => ModerationOps<E, R>;
161
+ /**
162
+ * The review queue's leaf half as a list filter: the `pending` rows, for
163
+ * the moderate rung only (`never` for anyone else). Pass it as a list
164
+ * request's `where`; the leaf's own read arms still apply.
165
+ *
166
+ * @since 0.1.0
167
+ */
168
+ export declare const reviewQueueFilter: (viewer: Viewer, policy: AccessPolicy, column?: string) => ListFilter<never>;
169
+ /**
170
+ * A classifier's report, shaped like effect-ai-ext's `ModerationModel`
171
+ * output: its own flag, and a score in `[0, 1]` per category it assessed.
172
+ * A category it didn't assess is absent, never `0`.
173
+ *
174
+ * @since 0.1.0
175
+ */
176
+ export interface ModerationScores {
177
+ readonly flagged: boolean;
178
+ readonly categoryScores: {
179
+ readonly [category: string]: number;
180
+ };
181
+ }
182
+ /**
183
+ * The band a flagged row lands in, from its worst breach.
184
+ *
185
+ * @since 0.1.0
186
+ */
187
+ export declare const ModerationPriority: Schema.Literals<readonly ["low", "medium", "high", "urgent"]>;
188
+ /**
189
+ * The {@link ModerationPriority} literal union.
190
+ *
191
+ * @since 0.1.0
192
+ */
193
+ export type ModerationPriority = typeof ModerationPriority.Type;
194
+ /**
195
+ * A graded report.
196
+ *
197
+ * @since 0.1.0
198
+ */
199
+ export interface ModerationGrade {
200
+ /** The classifier's own flag, or a threshold breach. */
201
+ readonly isFlagged: boolean;
202
+ /** The categories that cleared their threshold, sorted. */
203
+ readonly exceeded: ReadonlyArray<string>;
204
+ /** The highest score across every category, or `0` when none was scored. */
205
+ readonly topScore: number;
206
+ readonly priority: ModerationPriority;
207
+ }
208
+ /**
209
+ * The bar a category with no configured threshold is graded against;
210
+ * strictly above it flags. A starting point to tune, not a measured value.
211
+ *
212
+ * @since 0.1.0
213
+ */
214
+ export declare const DEFAULT_MODERATION_THRESHOLD = 0.5;
215
+ /**
216
+ * Grade a classifier's scores against the leaf's thresholds (a category
217
+ * missing from `thresholds` grades against `defaultThreshold`). A category
218
+ * breaches when its score is strictly above its bar; an unassessed one is
219
+ * absent and can't breach. The classifier's own flag is never overridden
220
+ * downward, only added to (raise a category's bar to `1` to ignore it). The
221
+ * band is the worst breaching score's, or the top score's for a flag with
222
+ * no breach. Pure.
223
+ *
224
+ * @example
225
+ * ```ts
226
+ * import { deepStrictEqual } from "node:assert";
227
+ * import { gradeScores } from "@thomasfosterau/pulpit/moderation";
228
+ *
229
+ * deepStrictEqual(
230
+ * gradeScores({ flagged: false, categoryScores: { hate: 0.8, spam: 0.3 } }, { spam: 0.2 }),
231
+ * { isFlagged: true, exceeded: ["hate", "spam"], topScore: 0.8, priority: "high" },
232
+ * );
233
+ * ```
234
+ *
235
+ * @since 0.1.0
236
+ */
237
+ export declare const gradeScores: (scores: ModerationScores, thresholds: {
238
+ readonly [category: string]: number;
239
+ }, defaultThreshold?: number) => ModerationGrade;
240
+ /**
241
+ * A report's status: `open` until the moderate rung resolves it (acted on)
242
+ * or dismisses it.
243
+ *
244
+ * @since 0.1.0
245
+ */
246
+ export declare const ReportStatus: Schema.Literals<readonly ["open", "resolved", "dismissed"]>;
247
+ /**
248
+ * The {@link ReportStatus} literal union.
249
+ *
250
+ * @since 0.1.0
251
+ */
252
+ export type ReportStatus = typeof ReportStatus.Type;
253
+ /**
254
+ * One member report, filed against a node.
255
+ *
256
+ * @since 0.1.0
257
+ */
258
+ export interface ReportRow {
259
+ readonly id: string;
260
+ readonly targetId: string;
261
+ readonly targetType: string;
262
+ readonly reporterId: string;
263
+ /** The application's reason code (`spam`, `harassment`, …). */
264
+ readonly reason: string;
265
+ readonly details: Option.Option<string>;
266
+ readonly status: ReportStatus;
267
+ readonly filedAt: Date;
268
+ readonly resolvedAt: Option.Option<Date>;
269
+ readonly resolvedBy: Option.Option<string>;
270
+ readonly resolutionNote: Option.Option<string>;
271
+ }
272
+ /**
273
+ * The write that closes an open report.
274
+ *
275
+ * @since 0.1.0
276
+ */
277
+ export interface ReportResolution {
278
+ readonly status: Exclude<ReportStatus, "open">;
279
+ readonly resolvedAt: Date;
280
+ readonly resolvedBy: string;
281
+ readonly resolutionNote: Option.Option<string>;
282
+ }
283
+ /**
284
+ * The report table an application supplies. It applies no policy, never
285
+ * opens a transaction, and fails only with its `E` (README → Seams).
286
+ *
287
+ * @since 0.1.0
288
+ */
289
+ export interface ReportStore<E, R> {
290
+ readonly mintId: Effect.Effect<string, E, R>;
291
+ readonly insert: (row: ReportRow) => Effect.Effect<void, E, R>;
292
+ readonly get: (id: string) => Effect.Effect<Option.Option<ReportRow>, E, R>;
293
+ /** One row, `FOR UPDATE`, so two resolutions serialise. */
294
+ readonly lock: (id: string) => Effect.Effect<Option.Option<ReportRow>, E, R>;
295
+ readonly close: (id: string, resolution: ReportResolution) => Effect.Effect<void, E, R>;
296
+ /**
297
+ * The page under `where`, newest first (`filed_at DESC, id DESC`), with
298
+ * the unpaged total. `where` reads `reporterId`, `targetId`, `targetType`
299
+ * and `status`.
300
+ */
301
+ readonly list: (query: {
302
+ readonly where: ListFilter<never>;
303
+ readonly limit: number;
304
+ readonly offset: number;
305
+ }) => Effect.Effect<Page<ReportRow>, E, R>;
306
+ }
307
+ declare const ReportNotFound_base: Schema.Class<ReportNotFound, Schema.TaggedStruct<"ReportNotFound", {
308
+ readonly id: Schema.String;
309
+ }>, import("effect/Cause").YieldableError & {
310
+ readonly brand: unique symbol;
311
+ }>;
312
+ /**
313
+ * No report at that id the viewer may read: the wire's 404.
314
+ *
315
+ * @since 0.1.0
316
+ */
317
+ export declare class ReportNotFound extends ReportNotFound_base {
318
+ }
319
+ declare const ReportClosed_base: Schema.Class<ReportClosed, Schema.TaggedStruct<"ReportClosed", {
320
+ readonly id: Schema.String;
321
+ readonly status: Schema.Literals<readonly ["open", "resolved", "dismissed"]>;
322
+ }>, import("effect/Cause").YieldableError & {
323
+ readonly brand: unique symbol;
324
+ }>;
325
+ /**
326
+ * The report is already resolved or dismissed: the wire's 409.
327
+ *
328
+ * @since 0.1.0
329
+ */
330
+ export declare class ReportClosed extends ReportClosed_base {
331
+ }
332
+ /**
333
+ * The operations {@link defineReports} produces.
334
+ *
335
+ * @since 0.1.0
336
+ */
337
+ export interface ReportOps<E, R> {
338
+ /**
339
+ * File a report against `targetId`. Fails `Unauthorized` when anonymous
340
+ * and `NodeNotFound` when the reporter can't `get` the target, never
341
+ * `Forbidden` (`SEC-AUTH-ENUM-001`), and `WriteRejected` (`RATE_LIMITED`)
342
+ * when the declared budget is spent. The answer is the reporter's own
343
+ * report: never whether others reported the same node.
344
+ */
345
+ readonly file: (input: {
346
+ readonly targetId: string;
347
+ readonly reason: string;
348
+ readonly details: Option.Option<string>;
349
+ }) => Effect.Effect<{
350
+ readonly id: string;
351
+ }, Unauthorized | NodeNotFound | WriteRejected | E, R>;
352
+ /** One report, for its reporter or the moderate rung; `ReportNotFound` otherwise. */
353
+ readonly get: (id: string) => Effect.Effect<ReportRow, ReportNotFound | E, R>;
354
+ /**
355
+ * One page, newest first: every report for the moderate rung (the queue's
356
+ * report half, by `status: open`), the viewer's own for anyone else.
357
+ */
358
+ readonly list: (request: {
359
+ readonly targetId: Option.Option<string>;
360
+ readonly status: Option.Option<ReportStatus>;
361
+ readonly limit: number;
362
+ readonly offset: number;
363
+ }) => Effect.Effect<Page<ReportRow>, E, R>;
364
+ /**
365
+ * Close an open report as `resolved` or `dismissed`. Fails `Unauthorized`
366
+ * / `Forbidden` below the moderate rung, before the report is read, then
367
+ * `ReportNotFound` and `ReportClosed`. Acting on the node is the target's
368
+ * own operation (`moderate`, a delete), called separately.
369
+ */
370
+ readonly resolve: (id: string, outcome: Exclude<ReportStatus, "open">, note: Option.Option<string>) => Effect.Effect<void, Unauthorized | Forbidden | ReportNotFound | ReportClosed | E, R>;
371
+ }
372
+ /**
373
+ * Bind member reports against one leaf: filed by a signed-in account that
374
+ * can read the target, read by their reporter and the moderate rung, and
375
+ * resolved by the moderate rung.
376
+ *
377
+ * @since 0.1.0
378
+ */
379
+ export declare const defineReports: <Resource, E, R>(definition: {
380
+ readonly target: SubtypeOps<Resource, E, R>;
381
+ readonly reports: ReportStore<E, R>;
382
+ readonly viewer: Effect.Effect<Viewer, never, R>;
383
+ readonly policy: AccessPolicy;
384
+ /**
385
+ * A write budget on `file` (pulpit#199, `./budgets`' `bindBudget`), spent
386
+ * as the `file` action once the reporter can read the target and before
387
+ * the report is stored. Omitted, reports are unthrottled.
388
+ */
389
+ readonly budget?: BoundBudget<E, R> | undefined;
390
+ }) => ReportOps<E, R>;
391
+ export {};
392
+ //# sourceMappingURL=moderation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"moderation.d.ts","sourceRoot":"","sources":["../src/moderation.ts"],"names":[],"mappings":"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"}