@thomasfosterau/pulpit 0.0.0 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +1271 -1
- package/dist/blobs.d.ts +249 -0
- package/dist/blobs.d.ts.map +1 -0
- package/dist/blobs.js +193 -0
- package/dist/blobs.js.map +1 -0
- package/dist/budgets.d.ts +255 -0
- package/dist/budgets.d.ts.map +1 -0
- package/dist/budgets.js +211 -0
- package/dist/budgets.js.map +1 -0
- package/dist/cache.d.ts +154 -0
- package/dist/cache.d.ts.map +1 -0
- package/dist/cache.js +118 -0
- package/dist/cache.js.map +1 -0
- package/dist/collections.d.ts +242 -0
- package/dist/collections.d.ts.map +1 -0
- package/dist/collections.js +215 -0
- package/dist/collections.js.map +1 -0
- package/dist/comments.d.ts +212 -0
- package/dist/comments.d.ts.map +1 -0
- package/dist/comments.js +192 -0
- package/dist/comments.js.map +1 -0
- package/dist/content.d.ts +231 -0
- package/dist/content.d.ts.map +1 -0
- package/dist/content.js +204 -0
- package/dist/content.js.map +1 -0
- package/dist/documents.d.ts +184 -0
- package/dist/documents.d.ts.map +1 -0
- package/dist/documents.js +190 -0
- package/dist/documents.js.map +1 -0
- package/dist/edges.d.ts +645 -0
- package/dist/edges.d.ts.map +1 -0
- package/dist/edges.js +558 -0
- package/dist/edges.js.map +1 -0
- package/dist/filter.d.ts +461 -0
- package/dist/filter.d.ts.map +1 -0
- package/dist/filter.js +387 -0
- package/dist/filter.js.map +1 -0
- package/dist/index.d.ts +133 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +133 -0
- package/dist/index.js.map +1 -0
- package/dist/internal/filter-columns.d.ts +8 -0
- package/dist/internal/filter-columns.d.ts.map +1 -0
- package/dist/internal/filter-columns.js +43 -0
- package/dist/internal/filter-columns.js.map +1 -0
- package/dist/internal/list-order.d.ts +26 -0
- package/dist/internal/list-order.d.ts.map +1 -0
- package/dist/internal/list-order.js +92 -0
- package/dist/internal/list-order.js.map +1 -0
- package/dist/internal/owned-columns.d.ts +93 -0
- package/dist/internal/owned-columns.d.ts.map +1 -0
- package/dist/internal/owned-columns.js +98 -0
- package/dist/internal/owned-columns.js.map +1 -0
- package/dist/internal/plain-rows.d.ts +32 -0
- package/dist/internal/plain-rows.d.ts.map +1 -0
- package/dist/internal/plain-rows.js +32 -0
- package/dist/internal/plain-rows.js.map +1 -0
- package/dist/internal/reference-columns.d.ts +80 -0
- package/dist/internal/reference-columns.d.ts.map +1 -0
- package/dist/internal/reference-columns.js +102 -0
- package/dist/internal/reference-columns.js.map +1 -0
- package/dist/moderation.d.ts +392 -0
- package/dist/moderation.d.ts.map +1 -0
- package/dist/moderation.js +375 -0
- package/dist/moderation.js.map +1 -0
- package/dist/paths.d.ts +204 -0
- package/dist/paths.d.ts.map +1 -0
- package/dist/paths.js +165 -0
- package/dist/paths.js.map +1 -0
- package/dist/proposals.d.ts +523 -0
- package/dist/proposals.d.ts.map +1 -0
- package/dist/proposals.js +505 -0
- package/dist/proposals.js.map +1 -0
- package/dist/publication.d.ts +148 -0
- package/dist/publication.d.ts.map +1 -0
- package/dist/publication.js +183 -0
- package/dist/publication.js.map +1 -0
- package/dist/purge.d.ts +224 -0
- package/dist/purge.d.ts.map +1 -0
- package/dist/purge.js +151 -0
- package/dist/purge.js.map +1 -0
- package/dist/reactions.d.ts +246 -0
- package/dist/reactions.d.ts.map +1 -0
- package/dist/reactions.js +183 -0
- package/dist/reactions.js.map +1 -0
- package/dist/registry.d.ts +316 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/registry.js +305 -0
- package/dist/registry.js.map +1 -0
- package/dist/search.d.ts +366 -0
- package/dist/search.d.ts.map +1 -0
- package/dist/search.js +273 -0
- package/dist/search.js.map +1 -0
- package/dist/social.d.ts +311 -0
- package/dist/social.d.ts.map +1 -0
- package/dist/social.js +205 -0
- package/dist/social.js.map +1 -0
- package/dist/subtype.d.ts +1613 -0
- package/dist/subtype.d.ts.map +1 -0
- package/dist/subtype.js +1300 -0
- package/dist/subtype.js.map +1 -0
- package/dist/testing.d.ts +675 -0
- package/dist/testing.d.ts.map +1 -0
- package/dist/testing.js +1022 -0
- package/dist/testing.js.map +1 -0
- package/dist/version-chain.d.ts +702 -0
- package/dist/version-chain.d.ts.map +1 -0
- package/dist/version-chain.js +489 -0
- package/dist/version-chain.js.map +1 -0
- package/dist/working-copy.d.ts +116 -0
- package/dist/working-copy.d.ts.map +1 -0
- package/dist/working-copy.js +95 -0
- package/dist/working-copy.js.map +1 -0
- package/package.json +138 -4
- package/src/blobs.ts +384 -0
- package/src/budgets.ts +338 -0
- package/src/cache.ts +218 -0
- package/src/collections.ts +427 -0
- package/src/comments.ts +392 -0
- package/src/content.ts +238 -0
- package/src/documents.ts +284 -0
- package/src/edges.ts +1185 -0
- package/src/filter.ts +523 -0
- package/src/index.ts +153 -0
- package/src/internal/filter-columns.ts +50 -0
- package/src/internal/list-order.ts +117 -0
- package/src/internal/owned-columns.ts +140 -0
- package/src/internal/plain-rows.ts +54 -0
- package/src/internal/reference-columns.ts +156 -0
- package/src/moderation.ts +728 -0
- package/src/paths.ts +267 -0
- package/src/proposals.ts +1014 -0
- package/src/publication.ts +225 -0
- package/src/purge.ts +313 -0
- package/src/reactions.ts +347 -0
- package/src/registry.ts +524 -0
- package/src/search.ts +540 -0
- package/src/social.ts +469 -0
- package/src/subtype.ts +3095 -0
- package/src/testing.ts +1873 -0
- package/src/version-chain.ts +1093 -0
- package/src/working-copy.ts +192 -0
package/dist/paths.js
ADDED
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* **Routable content** — a leaf's path claims, tied to its version chain,
|
|
3
|
+
* the slug aliases a path change leaves behind, and the gated resolution of
|
|
4
|
+
* a path to a node or a redirect.
|
|
5
|
+
*
|
|
6
|
+
* The behavioural spec is gardeneria's `apps/api/src/operations/pages.ts`
|
|
7
|
+
* and `packages/database/src/tables/pages.ts` (one live page per canonical
|
|
8
|
+
* path, old slugs kept in `page_slugs` and answered with a 301). A `#NNN`
|
|
9
|
+
* here names a gardeneria issue.
|
|
10
|
+
*
|
|
11
|
+
* ## The model (owner decision on pulpit#57)
|
|
12
|
+
*
|
|
13
|
+
* - **A claim lives on the root node**: one path per content item. The
|
|
14
|
+
* locale is chosen when the resolved node is read, never by the path, so a
|
|
15
|
+
* slug translation is not a claim of its own (pulpit#56's model (b)).
|
|
16
|
+
* - **Claims follow the node's lifecycle.** A write that leaves the node
|
|
17
|
+
* live claims the path its new state holds in the leaf's path column; a
|
|
18
|
+
* tombstone, or a state with no path, frees the claim; a restore re-claims
|
|
19
|
+
* its path, or fails {@link PathTaken} when another node holds it now.
|
|
20
|
+
* - **A path change records an alias**: the old path resolves as a redirect
|
|
21
|
+
* to the new one, for as long as no other node claims it.
|
|
22
|
+
* - **Resolution is gated** (`SEC-AUTH-ENUM-001`): {@link RoutableOps.resolve}
|
|
23
|
+
* reads the node through the leaf's own `get`, so a hidden node's path,
|
|
24
|
+
* and an alias to a hidden node, is {@link PathNotFound}, never a redirect.
|
|
25
|
+
*
|
|
26
|
+
* Free-form redirects (patterns, a 404 log) are the site's. The path's
|
|
27
|
+
* syntax is the leaf attribute's schema (effect-schema-ext's `Ids.slug`, once
|
|
28
|
+
* it publishes).
|
|
29
|
+
*
|
|
30
|
+
* @copyright Thomas Foster
|
|
31
|
+
* @module pulpit/paths
|
|
32
|
+
* @since 0.1.0
|
|
33
|
+
*/
|
|
34
|
+
import * as Effect from "effect/Effect";
|
|
35
|
+
import * as Equivalence from "effect/Equivalence";
|
|
36
|
+
import * as Option from "effect/Option";
|
|
37
|
+
import * as Predicate from "effect/Predicate";
|
|
38
|
+
import * as Schema from "effect/Schema";
|
|
39
|
+
// ── The path-store seam ───────────────────────────────────────────────
|
|
40
|
+
/**
|
|
41
|
+
* Another node holds the path canonically: the wire's 409. It names the
|
|
42
|
+
* path, never the holder, so it says only that the path is taken, as a
|
|
43
|
+
* unique-key conflict does, whoever holds it.
|
|
44
|
+
*
|
|
45
|
+
* @since 0.1.0
|
|
46
|
+
*/
|
|
47
|
+
export class PathTaken extends Schema.TaggedError()("PathTaken", {
|
|
48
|
+
path: Schema.String,
|
|
49
|
+
}) {
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* No node the viewer can read answers to the path: the wire's 404. A path
|
|
53
|
+
* nobody claims, one whose node is hidden from the viewer, and an alias to a
|
|
54
|
+
* hidden node all answer this, alike (`SEC-AUTH-ENUM-001`).
|
|
55
|
+
*
|
|
56
|
+
* @since 0.1.0
|
|
57
|
+
*/
|
|
58
|
+
export class PathNotFound extends Schema.TaggedError()("PathNotFound", {
|
|
59
|
+
path: Schema.String,
|
|
60
|
+
}) {
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Make a leaf routable (pulpit#57): its writes keep the node's path claim in
|
|
64
|
+
* step with its chain, and {@link RoutableOps.resolve} answers a path through
|
|
65
|
+
* the leaf's own read gate. After each write's append, in the same
|
|
66
|
+
* transaction, the claim follows the state the write landed at: a live state
|
|
67
|
+
* with a path claims it (a change leaves the old path as an alias; another
|
|
68
|
+
* node's claim fails {@link PathTaken}), and a tombstone or a state with no
|
|
69
|
+
* path frees it. A restore into a path another node took meanwhile therefore
|
|
70
|
+
* fails `PathTaken`, and its append rolls back with it. Wrap the result with
|
|
71
|
+
* `./subtype`'s `subtypeHandlers` as the leaf's own operations, so its
|
|
72
|
+
* `transact` covers the claims.
|
|
73
|
+
*
|
|
74
|
+
* @example
|
|
75
|
+
* ```ts
|
|
76
|
+
* import { strictEqual } from "node:assert";
|
|
77
|
+
* import * as Effect from "effect/Effect";
|
|
78
|
+
* import * as Option from "effect/Option";
|
|
79
|
+
* import { routable } from "@thomasfosterau/pulpit/paths";
|
|
80
|
+
* import { defineSubtype, nobodyFollows } from "@thomasfosterau/pulpit/subtype";
|
|
81
|
+
* import {
|
|
82
|
+
* makeInMemoryChainStore,
|
|
83
|
+
* makeInMemoryLeafStore,
|
|
84
|
+
* makeInMemoryPathStore,
|
|
85
|
+
* } from "@thomasfosterau/pulpit/testing";
|
|
86
|
+
* import * as HashSet from "effect/HashSet";
|
|
87
|
+
*
|
|
88
|
+
* const chain = makeInMemoryChainStore();
|
|
89
|
+
* const pages = routable(
|
|
90
|
+
* defineSubtype({
|
|
91
|
+
* type: "pages",
|
|
92
|
+
* access: { kind: "reference" },
|
|
93
|
+
* chain,
|
|
94
|
+
* leaves: makeInMemoryLeafStore(chain, { columns: ["slug"] }),
|
|
95
|
+
* viewer: Effect.succeed(Option.some({ id: "editor", role: "admin" })),
|
|
96
|
+
* policy: {
|
|
97
|
+
* canViewPrivate: () => true,
|
|
98
|
+
* canViewDeleted: () => true,
|
|
99
|
+
* canModerate: () => true,
|
|
100
|
+
* canWriteCorpus: () => true,
|
|
101
|
+
* },
|
|
102
|
+
* followers: nobodyFollows(HashSet.empty<string>()),
|
|
103
|
+
* toResource: (row) => ({ id: row.id, slug: row["slug"] }),
|
|
104
|
+
* }),
|
|
105
|
+
* { column: "slug", paths: makeInMemoryPathStore(), chain },
|
|
106
|
+
* );
|
|
107
|
+
* const program = Effect.gen(function* () {
|
|
108
|
+
* const { id } = yield* pages.create({ id: Option.none(), state: { slug: "about" } });
|
|
109
|
+
* yield* pages.update({ id, patch: { slug: "about-us" } });
|
|
110
|
+
* return yield* pages.resolve("about");
|
|
111
|
+
* });
|
|
112
|
+
* const resolved = Effect.runSync(program);
|
|
113
|
+
* strictEqual(resolved.kind === "redirect" && resolved.path, "about-us");
|
|
114
|
+
* ```
|
|
115
|
+
*
|
|
116
|
+
* @since 0.1.0
|
|
117
|
+
*/
|
|
118
|
+
export const routable = (ops, config) => {
|
|
119
|
+
const { column, paths, chain } = config;
|
|
120
|
+
// The claim the written state calls for: its path while it is live, none
|
|
121
|
+
// for a tombstone or a state whose path column holds no string.
|
|
122
|
+
const follow = ({ id, version }) => Effect.gen(function* () {
|
|
123
|
+
const written = yield* chain.snapshot(id, version);
|
|
124
|
+
const path = Option.flatMap(written, (snapshot) => snapshot.action === "delete"
|
|
125
|
+
? Option.none()
|
|
126
|
+
: Option.liftPredicate(snapshot.state[column], Predicate.isString));
|
|
127
|
+
const held = yield* paths.canonicalOf(id);
|
|
128
|
+
if (Option.makeEquivalence(Equivalence.String)(path, held))
|
|
129
|
+
return;
|
|
130
|
+
yield* Option.match(path, {
|
|
131
|
+
onNone: () => paths.release(id),
|
|
132
|
+
onSome: (claimed) => paths.claim(id, claimed),
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
const claiming = (write) => Effect.tap(write, follow);
|
|
136
|
+
const resolve = Effect.fn(`${ops.type}.resolve`)(function* (path) {
|
|
137
|
+
const notFound = new PathNotFound({ path });
|
|
138
|
+
const entry = yield* paths.lookup(path);
|
|
139
|
+
if (Option.isNone(entry))
|
|
140
|
+
return yield* notFound;
|
|
141
|
+
const { nodeId, canonical } = entry.value;
|
|
142
|
+
// The leaf's own gate: a hidden node answers exactly as no node.
|
|
143
|
+
const resource = yield* ops
|
|
144
|
+
.get(nodeId)
|
|
145
|
+
.pipe(Effect.catchTag("NodeNotFound", () => Effect.fail(notFound)));
|
|
146
|
+
if (canonical)
|
|
147
|
+
return { kind: "node", id: nodeId, resource };
|
|
148
|
+
const target = yield* paths.canonicalOf(nodeId);
|
|
149
|
+
return yield* Option.match(target, {
|
|
150
|
+
onNone: () => Effect.fail(notFound),
|
|
151
|
+
onSome: (to) => Effect.succeed({ kind: "redirect", id: nodeId, path: to }),
|
|
152
|
+
});
|
|
153
|
+
});
|
|
154
|
+
return {
|
|
155
|
+
...ops,
|
|
156
|
+
create: (input) => claiming(ops.create(input)),
|
|
157
|
+
update: (input) => claiming(ops.update(input)),
|
|
158
|
+
restore: (input) => claiming(ops.restore(input)),
|
|
159
|
+
// A delete over a tombstone wrote nothing, and its claim is already free.
|
|
160
|
+
delete: (id, precondition) => Effect.tap(ops.delete(id, precondition), (written) => written.alreadyDeleted ? Effect.void : paths.release(id)),
|
|
161
|
+
restoreVersion: (id, version, precondition) => claiming(ops.restoreVersion(id, version, precondition)),
|
|
162
|
+
resolve,
|
|
163
|
+
};
|
|
164
|
+
};
|
|
165
|
+
//# sourceMappingURL=paths.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"paths.js","sourceRoot":"","sources":["../src/paths.ts"],"names":[],"mappings":"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"}
|