@thomasfosterau/pulpit 0.0.0 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +1271 -1
- package/dist/blobs.d.ts +249 -0
- package/dist/blobs.d.ts.map +1 -0
- package/dist/blobs.js +193 -0
- package/dist/blobs.js.map +1 -0
- package/dist/budgets.d.ts +255 -0
- package/dist/budgets.d.ts.map +1 -0
- package/dist/budgets.js +211 -0
- package/dist/budgets.js.map +1 -0
- package/dist/cache.d.ts +154 -0
- package/dist/cache.d.ts.map +1 -0
- package/dist/cache.js +118 -0
- package/dist/cache.js.map +1 -0
- package/dist/collections.d.ts +242 -0
- package/dist/collections.d.ts.map +1 -0
- package/dist/collections.js +215 -0
- package/dist/collections.js.map +1 -0
- package/dist/comments.d.ts +212 -0
- package/dist/comments.d.ts.map +1 -0
- package/dist/comments.js +192 -0
- package/dist/comments.js.map +1 -0
- package/dist/content.d.ts +231 -0
- package/dist/content.d.ts.map +1 -0
- package/dist/content.js +204 -0
- package/dist/content.js.map +1 -0
- package/dist/documents.d.ts +184 -0
- package/dist/documents.d.ts.map +1 -0
- package/dist/documents.js +190 -0
- package/dist/documents.js.map +1 -0
- package/dist/edges.d.ts +645 -0
- package/dist/edges.d.ts.map +1 -0
- package/dist/edges.js +558 -0
- package/dist/edges.js.map +1 -0
- package/dist/filter.d.ts +461 -0
- package/dist/filter.d.ts.map +1 -0
- package/dist/filter.js +387 -0
- package/dist/filter.js.map +1 -0
- package/dist/index.d.ts +133 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +133 -0
- package/dist/index.js.map +1 -0
- package/dist/internal/filter-columns.d.ts +8 -0
- package/dist/internal/filter-columns.d.ts.map +1 -0
- package/dist/internal/filter-columns.js +43 -0
- package/dist/internal/filter-columns.js.map +1 -0
- package/dist/internal/list-order.d.ts +26 -0
- package/dist/internal/list-order.d.ts.map +1 -0
- package/dist/internal/list-order.js +92 -0
- package/dist/internal/list-order.js.map +1 -0
- package/dist/internal/owned-columns.d.ts +93 -0
- package/dist/internal/owned-columns.d.ts.map +1 -0
- package/dist/internal/owned-columns.js +98 -0
- package/dist/internal/owned-columns.js.map +1 -0
- package/dist/internal/plain-rows.d.ts +32 -0
- package/dist/internal/plain-rows.d.ts.map +1 -0
- package/dist/internal/plain-rows.js +32 -0
- package/dist/internal/plain-rows.js.map +1 -0
- package/dist/internal/reference-columns.d.ts +80 -0
- package/dist/internal/reference-columns.d.ts.map +1 -0
- package/dist/internal/reference-columns.js +102 -0
- package/dist/internal/reference-columns.js.map +1 -0
- package/dist/moderation.d.ts +392 -0
- package/dist/moderation.d.ts.map +1 -0
- package/dist/moderation.js +375 -0
- package/dist/moderation.js.map +1 -0
- package/dist/paths.d.ts +204 -0
- package/dist/paths.d.ts.map +1 -0
- package/dist/paths.js +165 -0
- package/dist/paths.js.map +1 -0
- package/dist/proposals.d.ts +523 -0
- package/dist/proposals.d.ts.map +1 -0
- package/dist/proposals.js +505 -0
- package/dist/proposals.js.map +1 -0
- package/dist/publication.d.ts +148 -0
- package/dist/publication.d.ts.map +1 -0
- package/dist/publication.js +183 -0
- package/dist/publication.js.map +1 -0
- package/dist/purge.d.ts +224 -0
- package/dist/purge.d.ts.map +1 -0
- package/dist/purge.js +151 -0
- package/dist/purge.js.map +1 -0
- package/dist/reactions.d.ts +246 -0
- package/dist/reactions.d.ts.map +1 -0
- package/dist/reactions.js +183 -0
- package/dist/reactions.js.map +1 -0
- package/dist/registry.d.ts +316 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/registry.js +305 -0
- package/dist/registry.js.map +1 -0
- package/dist/search.d.ts +366 -0
- package/dist/search.d.ts.map +1 -0
- package/dist/search.js +273 -0
- package/dist/search.js.map +1 -0
- package/dist/social.d.ts +311 -0
- package/dist/social.d.ts.map +1 -0
- package/dist/social.js +205 -0
- package/dist/social.js.map +1 -0
- package/dist/subtype.d.ts +1613 -0
- package/dist/subtype.d.ts.map +1 -0
- package/dist/subtype.js +1300 -0
- package/dist/subtype.js.map +1 -0
- package/dist/testing.d.ts +675 -0
- package/dist/testing.d.ts.map +1 -0
- package/dist/testing.js +1022 -0
- package/dist/testing.js.map +1 -0
- package/dist/version-chain.d.ts +702 -0
- package/dist/version-chain.d.ts.map +1 -0
- package/dist/version-chain.js +489 -0
- package/dist/version-chain.js.map +1 -0
- package/dist/working-copy.d.ts +116 -0
- package/dist/working-copy.d.ts.map +1 -0
- package/dist/working-copy.js +95 -0
- package/dist/working-copy.js.map +1 -0
- package/package.json +138 -4
- package/src/blobs.ts +384 -0
- package/src/budgets.ts +338 -0
- package/src/cache.ts +218 -0
- package/src/collections.ts +427 -0
- package/src/comments.ts +392 -0
- package/src/content.ts +238 -0
- package/src/documents.ts +284 -0
- package/src/edges.ts +1185 -0
- package/src/filter.ts +523 -0
- package/src/index.ts +153 -0
- package/src/internal/filter-columns.ts +50 -0
- package/src/internal/list-order.ts +117 -0
- package/src/internal/owned-columns.ts +140 -0
- package/src/internal/plain-rows.ts +54 -0
- package/src/internal/reference-columns.ts +156 -0
- package/src/moderation.ts +728 -0
- package/src/paths.ts +267 -0
- package/src/proposals.ts +1014 -0
- package/src/publication.ts +225 -0
- package/src/purge.ts +313 -0
- package/src/reactions.ts +347 -0
- package/src/registry.ts +524 -0
- package/src/search.ts +540 -0
- package/src/social.ts +469 -0
- package/src/subtype.ts +3095 -0
- package/src/testing.ts +1873 -0
- package/src/version-chain.ts +1093 -0
- package/src/working-copy.ts +192 -0
|
@@ -0,0 +1,505 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* **Suggested edits (proposals)** — a contributor's patch against a node
|
|
3
|
+
* they can read, stamped with the version it was written against, which
|
|
4
|
+
* the node's writer accepts (appending it) or rejects.
|
|
5
|
+
*
|
|
6
|
+
* The behavioural spec is gardeneria's `apps/api/src/operations/proposals.ts`,
|
|
7
|
+
* `proposal-patch.ts` and `proposal-conflict.ts` (#565), with their tests; a
|
|
8
|
+
* `#NNN` here names a gardeneria issue.
|
|
9
|
+
*
|
|
10
|
+
* ## The model (owner decisions on pulpit#49)
|
|
11
|
+
*
|
|
12
|
+
* - **A proposal is a plain row, not a node.** It carries the target, the
|
|
13
|
+
* patch as submitted, the `baseVersion` it was written against (stamped
|
|
14
|
+
* from the target's head, never client-supplied), its status (`pending |
|
|
15
|
+
* accepted | rejected | withdrawn`), the reviewer and the timestamps.
|
|
16
|
+
* Rejecting or withdrawing changes its status; only accepting writes to
|
|
17
|
+
* the target.
|
|
18
|
+
* - **Accepting appends through the target's own `update`** (`PUB-003`), as
|
|
19
|
+
* the account that accepted, so it runs the target's gate, its precondition
|
|
20
|
+
* and its before hooks, and lands as an ordinary `update` version
|
|
21
|
+
* (`VERSION-CHAIN-001/002`). The version records the proposer as its
|
|
22
|
+
* `proposedBy` fact beside that actor, so history shows both.
|
|
23
|
+
*
|
|
24
|
+
* ## Classification, in two tiers (#565, decision 2)
|
|
25
|
+
*
|
|
26
|
+
* - {@link classifyProposalBase} — `clean` or `moved`, from two version
|
|
27
|
+
* numbers in hand: no read, safe for every row of a list page.
|
|
28
|
+
* - {@link classifyProposalConflict} — `clean`, `stale` or `conflicting`,
|
|
29
|
+
* from the target's states at the base version and at the head
|
|
30
|
+
* (`diffStates`): two version reads, so only {@link ProposalOps.review}
|
|
31
|
+
* and {@link ProposalOps.accept} run it (`PERF-DB-003`).
|
|
32
|
+
*
|
|
33
|
+
* A touched attribute either state lacks (one kept in a side table, outside
|
|
34
|
+
* the chain) cannot be compared, so on a moved target it counts as
|
|
35
|
+
* conflicting: fail closed, since `stale` would let an accept overwrite a
|
|
36
|
+
* change nobody looked at.
|
|
37
|
+
*
|
|
38
|
+
* ## The gates
|
|
39
|
+
*
|
|
40
|
+
* - **Submit:** a signed-in account at the application's propose rung
|
|
41
|
+
* ({@link ProposalsDefinition.canPropose}) that can `get` the target; a
|
|
42
|
+
* target it can't read fails `NodeNotFound`, never `Forbidden`
|
|
43
|
+
* (`SEC-AUTH-ENUM-001`).
|
|
44
|
+
* - **Accept / reject / review:** the target's write gate (`requireWriter`:
|
|
45
|
+
* owner-or-moderator on an owned leaf, the corpus-write rung on a reference
|
|
46
|
+
* one), before the proposal's status is disclosed. Accept and review then
|
|
47
|
+
* read the two versions through the target's gated `compare` (`READ-009`).
|
|
48
|
+
* - **Withdraw:** the proposal's own author, while it is pending.
|
|
49
|
+
* - **Reads:** the moderate rung reads every proposal; any other signed-in
|
|
50
|
+
* account reads its own and those on targets it owns; the anonymous read
|
|
51
|
+
* none. A proposal is never public.
|
|
52
|
+
*
|
|
53
|
+
* ## A localised leaf (pulpit#184)
|
|
54
|
+
*
|
|
55
|
+
* On a leaf with a locale level (pulpit#56) a proposal may also carry a
|
|
56
|
+
* per-locale patch over the leaf's translated columns
|
|
57
|
+
* ({@link ProposalsDefinition.localeAttributes}), stored beside its shared
|
|
58
|
+
* patch and accepted through the same `update` as one version. Its conflict
|
|
59
|
+
* check reads each touched locale at the base and at the head: a touched
|
|
60
|
+
* translated column conflicts when it changed, when either version lacks it,
|
|
61
|
+
* or when the locale was added or dropped in between. A proposal may add a
|
|
62
|
+
* translation but never drop one (a `null` locale is refused): removing a
|
|
63
|
+
* locale is the owner's.
|
|
64
|
+
*
|
|
65
|
+
* A write budget (pulpit#199) is an opt-in: `budget` throttles `submit`.
|
|
66
|
+
* Notifications and the wire shape are the application's.
|
|
67
|
+
*
|
|
68
|
+
* @copyright Thomas Foster
|
|
69
|
+
* @module pulpit/proposals
|
|
70
|
+
* @since 0.1.0
|
|
71
|
+
*/
|
|
72
|
+
import * as Arr from "effect/Array";
|
|
73
|
+
import * as Effect from "effect/Effect";
|
|
74
|
+
import * as Option from "effect/Option";
|
|
75
|
+
import * as Predicate from "effect/Predicate";
|
|
76
|
+
import * as Record from "effect/Record";
|
|
77
|
+
import * as Schema from "effect/Schema";
|
|
78
|
+
import { always, eq, never, or } from "./filter.js";
|
|
79
|
+
import { nowDate, plainRowQuery, signedIn as signedInAs } from "./internal/plain-rows.js";
|
|
80
|
+
import { Forbidden, requireWriter, Unauthorized, } from "./subtype.js";
|
|
81
|
+
import { diffStates, localesOf, NodeNotFound, } from "./version-chain.js";
|
|
82
|
+
// ── Vocabulary ────────────────────────────────────────────────────────
|
|
83
|
+
/**
|
|
84
|
+
* A proposal's status. `pending` is the only open one; the other three are
|
|
85
|
+
* terminal, and a withdrawn proposal is recorded, not deleted.
|
|
86
|
+
*
|
|
87
|
+
* @since 0.1.0
|
|
88
|
+
*/
|
|
89
|
+
export const ProposalStatus = Schema.Literals(["pending", "accepted", "rejected", "withdrawn"]);
|
|
90
|
+
// ── Errors ────────────────────────────────────────────────────────────
|
|
91
|
+
/**
|
|
92
|
+
* No proposal at that id that the viewer may read: the wire's 404.
|
|
93
|
+
*
|
|
94
|
+
* @since 0.1.0
|
|
95
|
+
*/
|
|
96
|
+
export class ProposalNotFound extends Schema.TaggedError()("ProposalNotFound", {
|
|
97
|
+
id: Schema.String,
|
|
98
|
+
}) {
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* The proposal is no longer pending, so it can't be decided or withdrawn
|
|
102
|
+
* again: the wire's 409.
|
|
103
|
+
*
|
|
104
|
+
* @since 0.1.0
|
|
105
|
+
*/
|
|
106
|
+
export class ProposalClosed extends Schema.TaggedError()("ProposalClosed", {
|
|
107
|
+
id: Schema.String,
|
|
108
|
+
status: ProposalStatus,
|
|
109
|
+
}) {
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* The target changed underneath a touched attribute since the proposal was
|
|
113
|
+
* written (or one can't be compared), so accepting would overwrite an edit
|
|
114
|
+
* the reviewer never saw: the wire's 409. Reject it and resubmit against
|
|
115
|
+
* the new head.
|
|
116
|
+
*
|
|
117
|
+
* @since 0.1.0
|
|
118
|
+
*/
|
|
119
|
+
export class ProposalConflict extends Schema.TaggedError()("ProposalConflict", {
|
|
120
|
+
id: Schema.String,
|
|
121
|
+
attributes: Schema.Array(Schema.String),
|
|
122
|
+
/** The conflicting translated columns, by locale (pulpit#184); `{}` when none. */
|
|
123
|
+
locales: Schema.Record(Schema.String, Schema.Array(Schema.String)),
|
|
124
|
+
}) {
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* A patch that can't be proposed: not an object, empty, touching a
|
|
128
|
+
* forbidden or unknown attribute, or refused by the leaf's decoder. The
|
|
129
|
+
* wire's 422.
|
|
130
|
+
*
|
|
131
|
+
* @since 0.1.0
|
|
132
|
+
*/
|
|
133
|
+
export class InvalidProposalPatch extends Schema.TaggedError()("InvalidProposalPatch", { type: Schema.String, reason: Schema.String }) {
|
|
134
|
+
}
|
|
135
|
+
// ── Classification ────────────────────────────────────────────────────
|
|
136
|
+
/**
|
|
137
|
+
* The cheap verdict, from the base version and the target's head: `clean`
|
|
138
|
+
* only when they are equal.
|
|
139
|
+
*
|
|
140
|
+
* @example
|
|
141
|
+
* ```ts
|
|
142
|
+
* import { strictEqual } from "node:assert";
|
|
143
|
+
* import { classifyProposalBase } from "@thomasfosterau/pulpit/proposals";
|
|
144
|
+
*
|
|
145
|
+
* strictEqual(classifyProposalBase(4n, 4n), "clean");
|
|
146
|
+
* strictEqual(classifyProposalBase(4n, 5n), "moved");
|
|
147
|
+
* ```
|
|
148
|
+
*
|
|
149
|
+
* @since 0.1.0
|
|
150
|
+
*/
|
|
151
|
+
export const classifyProposalBase = (baseVersion, currentVersion) => (baseVersion === currentVersion ? "clean" : "moved");
|
|
152
|
+
/**
|
|
153
|
+
* The full verdict from the target's states at the base version and at the
|
|
154
|
+
* head, over the attributes the patch touches. Equal versions are `clean`
|
|
155
|
+
* without comparing anything. Otherwise a touched attribute conflicts when
|
|
156
|
+
* `diffStates` reports it changed (values compare structurally: a document
|
|
157
|
+
* by content, `null` against a value is a change, two `null`s are not) or
|
|
158
|
+
* when either state lacks it (fail closed).
|
|
159
|
+
*
|
|
160
|
+
* On a localised leaf, `touchedLocales` names the translated columns the
|
|
161
|
+
* proposal touches, by locale (pulpit#184), and each is read from the two
|
|
162
|
+
* versions' locale levels the same way. A locale neither version carries
|
|
163
|
+
* cannot conflict (a new translation); a locale only one carries (added or
|
|
164
|
+
* dropped in between) conflicts on every touched column. Pure.
|
|
165
|
+
*
|
|
166
|
+
* @example
|
|
167
|
+
* ```ts
|
|
168
|
+
* import { deepStrictEqual } from "node:assert";
|
|
169
|
+
* import { classifyProposalConflict } from "@thomasfosterau/pulpit/proposals";
|
|
170
|
+
*
|
|
171
|
+
* const at = (version: bigint, state: { readonly [column: string]: string }) => ({
|
|
172
|
+
* version,
|
|
173
|
+
* action: "update" as const,
|
|
174
|
+
* timestamp: new Date(0),
|
|
175
|
+
* state,
|
|
176
|
+
* });
|
|
177
|
+
* deepStrictEqual(
|
|
178
|
+
* classifyProposalConflict(at(4n, { name: "a", note: "x" }), at(5n, { name: "b", note: "x" }), [
|
|
179
|
+
* "name",
|
|
180
|
+
* "note",
|
|
181
|
+
* ]),
|
|
182
|
+
* { state: "conflicting", conflictingAttributes: ["name"], conflictingLocales: {} },
|
|
183
|
+
* );
|
|
184
|
+
* ```
|
|
185
|
+
*
|
|
186
|
+
* @since 0.1.0
|
|
187
|
+
*/
|
|
188
|
+
export const classifyProposalConflict = (base, current, touchedKeys, touchedLocales = {}) => {
|
|
189
|
+
if (base.version === current.version) {
|
|
190
|
+
return { state: "clean", conflictingAttributes: [], conflictingLocales: {} };
|
|
191
|
+
}
|
|
192
|
+
const conflictingAttributes = conflictsIn(base.state, current.state, touchedKeys);
|
|
193
|
+
const before = localesOf(base);
|
|
194
|
+
const after = localesOf(current);
|
|
195
|
+
const conflictingLocales = Record.filter(Record.map(touchedLocales, (keys, locale) => Option.match(Record.get(before, locale), {
|
|
196
|
+
// A locale neither version carries is a new translation; one only the head carries was added since.
|
|
197
|
+
onNone: () => (Record.has(after, locale) ? keys : []),
|
|
198
|
+
onSome: (from) => Option.match(Record.get(after, locale), {
|
|
199
|
+
// Dropped since the base.
|
|
200
|
+
onNone: () => keys,
|
|
201
|
+
onSome: (to) => conflictsIn(from, to, keys),
|
|
202
|
+
}),
|
|
203
|
+
})), Arr.isReadonlyArrayNonEmpty);
|
|
204
|
+
return Arr.isReadonlyArrayNonEmpty(conflictingAttributes) ||
|
|
205
|
+
!Record.isEmptyRecord(conflictingLocales)
|
|
206
|
+
? { state: "conflicting", conflictingAttributes, conflictingLocales }
|
|
207
|
+
: { state: "stale", conflictingAttributes: [], conflictingLocales: {} };
|
|
208
|
+
};
|
|
209
|
+
/** The touched `keys` that changed from `from` to `to`, or that either lacks (fail closed). */
|
|
210
|
+
const conflictsIn = (from, to, keys) => {
|
|
211
|
+
const changes = diffStates(from, to);
|
|
212
|
+
return Arr.filter(keys, (key) => !Record.has(from, key) || !Record.has(to, key) || Record.has(changes, key));
|
|
213
|
+
};
|
|
214
|
+
// ── Patch validation ──────────────────────────────────────────────────
|
|
215
|
+
/**
|
|
216
|
+
* The attributes no proposal may carry, whatever the leaf declares:
|
|
217
|
+
* identity (`id`, `type`) and the publication axes (`visibility`, `draft`,
|
|
218
|
+
* `publishedAt`), which are the owner's to set (`PUB-005`). An application
|
|
219
|
+
* adds its own through {@link ProposalsDefinition.forbidden} (gardeneria
|
|
220
|
+
* adds `slug`, a node's public URL).
|
|
221
|
+
*
|
|
222
|
+
* @since 0.1.0
|
|
223
|
+
*/
|
|
224
|
+
export const PROPOSAL_FORBIDDEN_KEYS = [
|
|
225
|
+
"id",
|
|
226
|
+
"type",
|
|
227
|
+
"visibility",
|
|
228
|
+
"draft",
|
|
229
|
+
"publishedAt",
|
|
230
|
+
];
|
|
231
|
+
const quoted = (keys) => Arr.map(keys, (key) => `\`${key}\``).join(", ");
|
|
232
|
+
/**
|
|
233
|
+
* Why `keys` is not an admissible key set, or none. Forbidden is checked
|
|
234
|
+
* before unknown, so a `visibility` patch reads as a policy refusal, not a
|
|
235
|
+
* typo, even on a leaf that doesn't declare the attribute.
|
|
236
|
+
*/
|
|
237
|
+
const keyViolation = (keys, attributes, forbidden) => {
|
|
238
|
+
const refused = Arr.filter(keys, (key) => Arr.contains(forbidden, key));
|
|
239
|
+
if (Arr.isReadonlyArrayNonEmpty(refused)) {
|
|
240
|
+
return Option.some(`${quoted(refused)} can't be proposed: identity and publication are the owner's to set`);
|
|
241
|
+
}
|
|
242
|
+
const unknown = Arr.filter(keys, (key) => !Arr.contains(attributes, key));
|
|
243
|
+
return Arr.isReadonlyArrayNonEmpty(unknown)
|
|
244
|
+
? Option.some(`${quoted(unknown)} is not a proposable attribute`)
|
|
245
|
+
: Option.none();
|
|
246
|
+
};
|
|
247
|
+
/** A JSON object's members, or none for an array, a scalar or `null`. */
|
|
248
|
+
const jsonMembers = (value) => value === null ||
|
|
249
|
+
Predicate.isBoolean(value) ||
|
|
250
|
+
Predicate.isNumber(value) ||
|
|
251
|
+
Predicate.isString(value) ||
|
|
252
|
+
Arr.isArray(value)
|
|
253
|
+
? Option.none()
|
|
254
|
+
: Option.some(Record.toEntries(value));
|
|
255
|
+
/**
|
|
256
|
+
* Check a submitted patch's shape and keys against the leaf's proposable
|
|
257
|
+
* `attributes` (its update fields, in declaration order) and the
|
|
258
|
+
* `forbidden` keys, then decode it with the leaf's own decoder. Unknown keys
|
|
259
|
+
* are refused loudly rather than dropped (a misspelt attribute would
|
|
260
|
+
* otherwise accept and change nothing). Run at submit and again at accept,
|
|
261
|
+
* since the leaf's schema may have moved in between. Fails
|
|
262
|
+
* {@link InvalidProposalPatch}.
|
|
263
|
+
*
|
|
264
|
+
* On a localised leaf, `options.locales` is the per-locale patch as
|
|
265
|
+
* submitted (pulpit#184): an object from a non-empty locale tag to an object
|
|
266
|
+
* of changes to `options.localeAttributes` (the translated columns), each
|
|
267
|
+
* checked and decoded as the shared patch is. A `null` locale (dropping a
|
|
268
|
+
* translation) is refused, as is any locale on a leaf with no
|
|
269
|
+
* `localeAttributes`. The shared patch may then be `{}`, so long as some
|
|
270
|
+
* locale changes something.
|
|
271
|
+
*
|
|
272
|
+
* @since 0.1.0
|
|
273
|
+
*/
|
|
274
|
+
export const validateProposalPatch = (type, patch, options) => {
|
|
275
|
+
const invalid = (reason) => Effect.fail(new InvalidProposalPatch({ type, reason }));
|
|
276
|
+
if (!Predicate.isObject(patch) || Arr.isArray(patch)) {
|
|
277
|
+
return invalid("expected an object of attribute changes");
|
|
278
|
+
}
|
|
279
|
+
const localeMembers = jsonMembers(options.locales ?? {});
|
|
280
|
+
if (Option.isNone(localeMembers))
|
|
281
|
+
return invalid("expected an object of changes per locale");
|
|
282
|
+
const keys = Record.keys(patch);
|
|
283
|
+
const locales = localeMembers.value;
|
|
284
|
+
if (!Arr.isReadonlyArrayNonEmpty(keys) && !Arr.isReadonlyArrayNonEmpty(locales)) {
|
|
285
|
+
return invalid("it proposes no changes");
|
|
286
|
+
}
|
|
287
|
+
const translated = options.localeAttributes ?? [];
|
|
288
|
+
const localeProblem = Arr.findFirst(locales, ([locale, changes]) => localeViolation(locale, changes, translated, options.forbidden));
|
|
289
|
+
if (Option.isSome(localeProblem))
|
|
290
|
+
return invalid(localeProblem.value);
|
|
291
|
+
return Option.match(keyViolation(keys, options.attributes, options.forbidden), {
|
|
292
|
+
onSome: invalid,
|
|
293
|
+
onNone: () => Effect.gen(function* () {
|
|
294
|
+
const decoded = Arr.isReadonlyArrayNonEmpty(keys) ? yield* options.decode(patch) : {};
|
|
295
|
+
const decodedLocales = yield* Effect.forEach(locales, ([locale, changes]) => Effect.map(options.decode(changes), (decodedChanges) => [locale, decodedChanges]), { concurrency: 1 });
|
|
296
|
+
return {
|
|
297
|
+
patch: decoded,
|
|
298
|
+
touchedKeys: Arr.filter(options.attributes, (key) => Arr.contains(keys, key)),
|
|
299
|
+
locales: Record.fromEntries(decodedLocales),
|
|
300
|
+
touchedLocales: Record.fromEntries(Arr.map(locales, ([locale, changes]) => {
|
|
301
|
+
const touched = Option.match(jsonMembers(changes), {
|
|
302
|
+
onNone: () => [],
|
|
303
|
+
onSome: Arr.map(([key]) => key),
|
|
304
|
+
});
|
|
305
|
+
return [locale, Arr.filter(translated, (key) => Arr.contains(touched, key))];
|
|
306
|
+
})),
|
|
307
|
+
};
|
|
308
|
+
}),
|
|
309
|
+
});
|
|
310
|
+
};
|
|
311
|
+
/** Why one locale's entry of a per-locale patch is not admissible, or none. */
|
|
312
|
+
const localeViolation = (locale, changes, translated, forbidden) => {
|
|
313
|
+
if (!Arr.isReadonlyArrayNonEmpty(translated)) {
|
|
314
|
+
return Option.some("this leaf takes no translated attributes");
|
|
315
|
+
}
|
|
316
|
+
if (locale === "")
|
|
317
|
+
return Option.some("a locale tag is empty");
|
|
318
|
+
if (changes === null) {
|
|
319
|
+
return Option.some(`dropping the \`${locale}\` translation is the owner's to do`);
|
|
320
|
+
}
|
|
321
|
+
const members = jsonMembers(changes);
|
|
322
|
+
if (Option.isNone(members)) {
|
|
323
|
+
return Option.some(`expected an object of attribute changes for \`${locale}\``);
|
|
324
|
+
}
|
|
325
|
+
const keys = Arr.map(members.value, ([key]) => key);
|
|
326
|
+
if (!Arr.isReadonlyArrayNonEmpty(keys)) {
|
|
327
|
+
return Option.some(`it proposes no changes for \`${locale}\``);
|
|
328
|
+
}
|
|
329
|
+
return Option.map(keyViolation(keys, translated, forbidden), (reason) => `${reason} (in \`${locale}\`)`);
|
|
330
|
+
};
|
|
331
|
+
const noNote = { note: Option.none() };
|
|
332
|
+
/** The cheap verdict, while pending and the target has a head. */
|
|
333
|
+
const viewOf = ({ row, targetVersion }) => ({
|
|
334
|
+
proposal: row,
|
|
335
|
+
baseState: row.status === "pending"
|
|
336
|
+
? Option.map(targetVersion, (head) => classifyProposalBase(row.baseVersion, head))
|
|
337
|
+
: Option.none(),
|
|
338
|
+
});
|
|
339
|
+
/**
|
|
340
|
+
* Bind one leaf's proposal operations. Every operation is traced as
|
|
341
|
+
* `${type}.proposals.<operation>`, and the writes run in the caller's
|
|
342
|
+
* transaction: an accept's append and its close commit or roll back
|
|
343
|
+
* together. An accept runs the target's `update`, so its before hooks, but
|
|
344
|
+
* not `subtypeHandlers`' write-through: an application that caches the
|
|
345
|
+
* target writes the accepted version through itself.
|
|
346
|
+
*
|
|
347
|
+
* @since 0.1.0
|
|
348
|
+
*/
|
|
349
|
+
export const defineProposals = (definition) => {
|
|
350
|
+
const { type, target, proposals, policy } = definition;
|
|
351
|
+
const forbidden = definition.forbidden ?? PROPOSAL_FORBIDDEN_KEYS;
|
|
352
|
+
const validate = (patch, locales) => validateProposalPatch(type, patch, {
|
|
353
|
+
attributes: definition.attributes,
|
|
354
|
+
forbidden,
|
|
355
|
+
decode: definition.decode,
|
|
356
|
+
locales: Option.getOrUndefined(locales),
|
|
357
|
+
localeAttributes: definition.localeAttributes,
|
|
358
|
+
});
|
|
359
|
+
const signedIn = signedInAs(definition.viewer);
|
|
360
|
+
/** The target's write gate: before any proposal state is disclosed to the caller. */
|
|
361
|
+
const writerOf = (targetId) => Effect.flatMap(definition.viewer, (viewer) => requireWriter(target.access, viewer, policy, { type, id: targetId }, definition.ownerOf(targetId)));
|
|
362
|
+
/** The locked row, the target's write gate passed, still pending. */
|
|
363
|
+
const pendingForDecision = (id) => Effect.gen(function* () {
|
|
364
|
+
const row = yield* proposals.lock(id);
|
|
365
|
+
if (Option.isNone(row) || row.value.targetType !== type) {
|
|
366
|
+
return yield* new ProposalNotFound({ id });
|
|
367
|
+
}
|
|
368
|
+
const account = yield* writerOf(row.value.targetId);
|
|
369
|
+
if (row.value.status !== "pending") {
|
|
370
|
+
return yield* new ProposalClosed({ id, status: row.value.status });
|
|
371
|
+
}
|
|
372
|
+
return { row: row.value, account };
|
|
373
|
+
});
|
|
374
|
+
/** The target's head entry, or `NodeNotFound`. */
|
|
375
|
+
const headOf = (targetId) => Effect.flatMap(definition.chain.head(targetId), (head) => Effect.fromOption(head, () => new NodeNotFound({ type, id: targetId })));
|
|
376
|
+
/** Re-validate the stored patch and classify it, through the target's gated `compare`. */
|
|
377
|
+
const classify = (row, head) => Effect.gen(function* () {
|
|
378
|
+
const validated = yield* validate(row.patch, row.locales);
|
|
379
|
+
const comparison = yield* target.compare(row.targetId, row.baseVersion, head);
|
|
380
|
+
return {
|
|
381
|
+
validated,
|
|
382
|
+
conflict: classifyProposalConflict(comparison.from, comparison.to, validated.touchedKeys, validated.touchedLocales),
|
|
383
|
+
};
|
|
384
|
+
});
|
|
385
|
+
const close = (id, status, decidedBy, note, appliedVersion = Option.none()) => Effect.flatMap(nowDate, (decidedAt) => proposals.close(id, { status, decidedAt, decidedBy, decisionNote: note, appliedVersion }));
|
|
386
|
+
const submit = Effect.fn(`${type}.proposals.submit`)(function* (input) {
|
|
387
|
+
const account = yield* signedIn;
|
|
388
|
+
if (!definition.canPropose(account))
|
|
389
|
+
return yield* new Forbidden();
|
|
390
|
+
// A target the proposer can't read is missing to them (SEC-AUTH-ENUM-001).
|
|
391
|
+
yield* target.get(input.targetId);
|
|
392
|
+
const locales = Option.fromUndefinedOr(input.locales);
|
|
393
|
+
yield* validate(input.patch, locales);
|
|
394
|
+
if (definition.budget !== undefined) {
|
|
395
|
+
yield* definition.budget.spend(Option.some(account), "submit", input.targetId);
|
|
396
|
+
}
|
|
397
|
+
const head = yield* headOf(input.targetId);
|
|
398
|
+
const id = yield* proposals.mintId;
|
|
399
|
+
yield* proposals.insert({
|
|
400
|
+
id,
|
|
401
|
+
targetId: input.targetId,
|
|
402
|
+
targetType: type,
|
|
403
|
+
authorId: account.id,
|
|
404
|
+
patch: input.patch,
|
|
405
|
+
locales,
|
|
406
|
+
rationale: input.rationale,
|
|
407
|
+
baseVersion: head.version,
|
|
408
|
+
status: "pending",
|
|
409
|
+
submittedAt: yield* nowDate,
|
|
410
|
+
decidedAt: Option.none(),
|
|
411
|
+
decidedBy: Option.none(),
|
|
412
|
+
decisionNote: Option.none(),
|
|
413
|
+
appliedVersion: Option.none(),
|
|
414
|
+
});
|
|
415
|
+
return { id };
|
|
416
|
+
});
|
|
417
|
+
/** Whether the viewer may read `row`: the moderate rung, its author, or the target's owner. */
|
|
418
|
+
const readable = (viewer, row) => Option.match(viewer, {
|
|
419
|
+
onNone: () => Effect.succeed(false),
|
|
420
|
+
onSome: (account) => policy.canModerate(account) || account.id === row.authorId
|
|
421
|
+
? Effect.succeed(true)
|
|
422
|
+
: Effect.map(definition.ownerOf(row.targetId), (owner) => Option.contains(owner, account.id)),
|
|
423
|
+
});
|
|
424
|
+
const get = Effect.fn(`${type}.proposals.get`)(function* (id) {
|
|
425
|
+
const viewer = yield* definition.viewer;
|
|
426
|
+
const listing = yield* proposals.get(id);
|
|
427
|
+
if (Option.isNone(listing) ||
|
|
428
|
+
listing.value.row.targetType !== type ||
|
|
429
|
+
!(yield* readable(viewer, listing.value.row))) {
|
|
430
|
+
return yield* new ProposalNotFound({ id });
|
|
431
|
+
}
|
|
432
|
+
return viewOf(listing.value);
|
|
433
|
+
});
|
|
434
|
+
/** The read scope as a list filter: the same three facts as `readable`. */
|
|
435
|
+
const readFilter = (viewer) => Option.match(viewer, {
|
|
436
|
+
onNone: () => never,
|
|
437
|
+
onSome: (account) => policy.canModerate(account)
|
|
438
|
+
? always
|
|
439
|
+
: or(eq("authorId", account.id), eq("targetOwnerId", account.id)),
|
|
440
|
+
});
|
|
441
|
+
const list = Effect.fn(`${type}.proposals.list`)(function* (request) {
|
|
442
|
+
const viewer = yield* definition.viewer;
|
|
443
|
+
const page = yield* proposals.list(plainRowQuery(type, readFilter(viewer), request));
|
|
444
|
+
return { rows: Arr.map(page.rows, viewOf), total: page.total };
|
|
445
|
+
});
|
|
446
|
+
const review = Effect.fn(`${type}.proposals.review`)(function* (id) {
|
|
447
|
+
const listing = yield* proposals.get(id);
|
|
448
|
+
if (Option.isNone(listing) || listing.value.row.targetType !== type) {
|
|
449
|
+
return yield* new ProposalNotFound({ id });
|
|
450
|
+
}
|
|
451
|
+
const { row } = listing.value;
|
|
452
|
+
yield* writerOf(row.targetId);
|
|
453
|
+
if (row.status !== "pending")
|
|
454
|
+
return yield* new ProposalClosed({ id, status: row.status });
|
|
455
|
+
const head = yield* headOf(row.targetId);
|
|
456
|
+
const { conflict } = yield* classify(row, head.version);
|
|
457
|
+
return {
|
|
458
|
+
proposal: row,
|
|
459
|
+
baseState: Option.some(classifyProposalBase(row.baseVersion, head.version)),
|
|
460
|
+
conflict,
|
|
461
|
+
};
|
|
462
|
+
});
|
|
463
|
+
const accept = Effect.fn(`${type}.proposals.accept`)(function* (id, options = noNote) {
|
|
464
|
+
const { row, account } = yield* pendingForDecision(id);
|
|
465
|
+
const head = yield* headOf(row.targetId);
|
|
466
|
+
const { validated, conflict } = yield* classify(row, head.version);
|
|
467
|
+
if (conflict.state === "conflicting") {
|
|
468
|
+
return yield* new ProposalConflict({
|
|
469
|
+
id,
|
|
470
|
+
attributes: conflict.conflictingAttributes,
|
|
471
|
+
locales: conflict.conflictingLocales,
|
|
472
|
+
});
|
|
473
|
+
}
|
|
474
|
+
// Through the target's own update (PUB-003), expecting the head it was
|
|
475
|
+
// classified against: an edit landing in between fails StaleVersion.
|
|
476
|
+
const written = yield* target.update({
|
|
477
|
+
id: row.targetId,
|
|
478
|
+
patch: validated.patch,
|
|
479
|
+
locales: validated.locales,
|
|
480
|
+
expectedVersion: head.version,
|
|
481
|
+
proposedBy: Option.some(row.authorId),
|
|
482
|
+
});
|
|
483
|
+
yield* close(id, "accepted", account.id, options.note, Option.some(written.version));
|
|
484
|
+
return written;
|
|
485
|
+
});
|
|
486
|
+
const reject = Effect.fn(`${type}.proposals.reject`)(function* (id, options = noNote) {
|
|
487
|
+
const { account } = yield* pendingForDecision(id);
|
|
488
|
+
yield* close(id, "rejected", account.id, options.note);
|
|
489
|
+
});
|
|
490
|
+
const withdraw = Effect.fn(`${type}.proposals.withdraw`)(function* (id) {
|
|
491
|
+
const account = yield* signedIn;
|
|
492
|
+
const row = yield* proposals.lock(id);
|
|
493
|
+
if (Option.isNone(row) || row.value.targetType !== type) {
|
|
494
|
+
return yield* new ProposalNotFound({ id });
|
|
495
|
+
}
|
|
496
|
+
if (row.value.authorId !== account.id)
|
|
497
|
+
return yield* new Forbidden();
|
|
498
|
+
if (row.value.status !== "pending") {
|
|
499
|
+
return yield* new ProposalClosed({ id, status: row.value.status });
|
|
500
|
+
}
|
|
501
|
+
yield* close(id, "withdrawn", account.id, Option.none());
|
|
502
|
+
});
|
|
503
|
+
return { submit, get, list, review, accept, reject, withdraw };
|
|
504
|
+
};
|
|
505
|
+
//# sourceMappingURL=proposals.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"proposals.js","sourceRoot":"","sources":["../src/proposals.ts"],"names":[],"mappings":"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"}
|