@valbuild/shared 0.145.1 → 0.146.2
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/CHANGELOG.md +46 -0
- package/dist/declarations/src/internal/ApiRoutes.d.ts +130 -273
- package/dist/declarations/src/internal/chainHead.d.ts +5 -7
- package/dist/declarations/src/internal/index.d.ts +4 -0
- package/dist/declarations/src/internal/patchSets/PatchSets.d.ts +75 -0
- package/dist/declarations/src/internal/patchSets/schemaTypesOfPath.d.ts +5 -0
- package/dist/declarations/src/internal/patchSets/selectionClosure.d.ts +23 -0
- package/dist/declarations/src/internal/proposalMerge/trees.d.ts +56 -0
- package/dist/declarations/src/internal/publish/contentApi.d.ts +10 -4
- package/dist/declarations/src/internal/resolveSchemaSourceFixes.d.ts +3 -2
- package/dist/declarations/src/internal/server/requestScopedMemo.d.ts +10 -8
- package/dist/declarations/src/internal/server/types.d.ts +3 -3
- package/dist/declarations/src/internal/zod/History.d.ts +5 -5
- package/dist/declarations/src/internal/zod/Patch.d.ts +0 -1
- package/dist/declarations/src/internal/zod/SerializedSchema.d.ts +1 -1
- package/internal/dist/valbuild-shared-internal.cjs.dev.js +770 -274
- package/internal/dist/valbuild-shared-internal.cjs.prod.js +770 -274
- package/internal/dist/valbuild-shared-internal.esm.js +763 -274
- package/package.json +2 -2
|
@@ -5,13 +5,11 @@ import type { PatchId } from "@valbuild/core";
|
|
|
5
5
|
*
|
|
6
6
|
* The head the server REPORTS (`headPatchId`) when it reports one, and not the
|
|
7
7
|
* last patch it listed. The list — `/applicable/patches`, and `/stat` on top of
|
|
8
|
-
* it — leaves out patches the running deployment already contains, and
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* — and a re-sync returns the same list. What fixed it in the Studio before
|
|
14
|
-
* this existed was discarding the other author's change.
|
|
8
|
+
* it — leaves out patches the running deployment already contains, and a
|
|
9
|
+
* publish can ship later patches while an earlier one stays pending (a failed
|
|
10
|
+
* publish holds its changes back). The last listed id is then behind the head
|
|
11
|
+
* for good, and the content service accepts a write only on the head, so every
|
|
12
|
+
* save naming it is refused — and a re-sync returns the same list.
|
|
15
13
|
*
|
|
16
14
|
* `null` is a REPORTED empty chain, not "unknown". `undefined` is a server
|
|
17
15
|
* that reports no head (`fs` mode, which ignores the parent anyway, or a
|
|
@@ -37,3 +37,7 @@ export * from "./search/sourcePath.js";
|
|
|
37
37
|
export * from "./theme/accentRamp.js";
|
|
38
38
|
export * from "./publish/contentApi.js";
|
|
39
39
|
export * from "./publish/protocol.js";
|
|
40
|
+
export * from "./patchSets/PatchSets.js";
|
|
41
|
+
export * from "./patchSets/selectionClosure.js";
|
|
42
|
+
export * from "./patchSets/schemaTypesOfPath.js";
|
|
43
|
+
export * from "./proposalMerge/trees.js";
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { ModuleFilePath, PatchId, SerializedSchema } from "@valbuild/core";
|
|
2
|
+
import { Operation } from "@valbuild/core/patch";
|
|
3
|
+
type AuthorId = string;
|
|
4
|
+
type IsoDateString = string;
|
|
5
|
+
export type PatchMetadata = {
|
|
6
|
+
patchId: PatchId;
|
|
7
|
+
patchPath: PatchPath;
|
|
8
|
+
opType: Operation["op"];
|
|
9
|
+
schemaTypes: Array<SerializedSchema["type"]>;
|
|
10
|
+
author: AuthorId | null;
|
|
11
|
+
createdAt: IsoDateString;
|
|
12
|
+
};
|
|
13
|
+
export type PatchSetMetadata = {
|
|
14
|
+
moduleFilePath: ModuleFilePath;
|
|
15
|
+
patchPath: PatchPath;
|
|
16
|
+
patches: PatchMetadata[];
|
|
17
|
+
authors: AuthorId[];
|
|
18
|
+
opTypes: Operation["op"][];
|
|
19
|
+
schemaTypes: Array<SerializedSchema["type"]>;
|
|
20
|
+
lastUpdated: IsoDateString;
|
|
21
|
+
lastUpdatedBy: AuthorId | null;
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* A patch set is a set of patches that are non-independent of each other. This is useful for example when we want to apply a set of patches atomically.
|
|
25
|
+
* A patch set is identified by a module file path and a json patch path delimited by a '?' character. We call this the patch set path. Example: /file/name.val.ts?/path/to/patch
|
|
26
|
+
*
|
|
27
|
+
*
|
|
28
|
+
* NOTE: the order of patch sets is always newest first. This is because the UX always shows the newest patches first.
|
|
29
|
+
*/
|
|
30
|
+
export type SerializedPatchSet = PatchSetMetadata[];
|
|
31
|
+
type PatchPath = string[];
|
|
32
|
+
/**
|
|
33
|
+
* Is `inner` strictly nested inside the patch set `outer`?
|
|
34
|
+
*
|
|
35
|
+
* A plain `inner.startsWith(outer)` is not enough, and the difference is not
|
|
36
|
+
* cosmetic. The file part of a key is terminated by the '?' delimiter, but the
|
|
37
|
+
* segments of the patch path are not terminated by anything, so a raw prefix
|
|
38
|
+
* test also matches siblings whose *name* merely starts with the same
|
|
39
|
+
* characters: '/p.val.ts?foobar/title' starts with '/p.val.ts?foo', so removing
|
|
40
|
+
* record key "foo" and retitling record key "foobar" would be merged into one
|
|
41
|
+
* patch set.
|
|
42
|
+
*
|
|
43
|
+
* That used to only over-group two unrelated changes in the compare view. Now
|
|
44
|
+
* that patch sets decide what moves between branches as one (whole patch sets,
|
|
45
|
+
* as `changesToMove` moves them), it means moving one key silently moves its
|
|
46
|
+
* similarly-named sibling — so the boundary has to be
|
|
47
|
+
* checked explicitly: '?' when `outer` is a bare module file path, '/' when it
|
|
48
|
+
* already has a patch path.
|
|
49
|
+
*/
|
|
50
|
+
export declare function isInsidePatchSetPath(inner: string, outer: string): boolean;
|
|
51
|
+
export declare class PatchSets {
|
|
52
|
+
private insertedPatches;
|
|
53
|
+
private patchSetMetadata;
|
|
54
|
+
private orderedInsertKeys;
|
|
55
|
+
constructor();
|
|
56
|
+
reset(): void;
|
|
57
|
+
getInsertedPatches(): Set<PatchId>;
|
|
58
|
+
private insertPath;
|
|
59
|
+
/**
|
|
60
|
+
* Insert a whole patch.
|
|
61
|
+
*
|
|
62
|
+
* Takes the patch rather than one op because the de-duplication below is
|
|
63
|
+
* per-*patch*: a patch is either known to this instance or it is not. When this
|
|
64
|
+
* took a single op, callers looped and every call after the first hit the
|
|
65
|
+
* `insertedPatches` guard and returned — so only a patch's first op ever reached
|
|
66
|
+
* a patch set. A patch that touched two places (a `move`, or a `file` op sitting
|
|
67
|
+
* before its source op) was therefore mis-grouped, which is invisible in the
|
|
68
|
+
* compare view but decides what moves as one.
|
|
69
|
+
*/
|
|
70
|
+
insert(moduleFilePath: ModuleFilePath, schema: SerializedSchema | undefined, patch: Operation[], patchId: PatchId, createdAt: IsoDateString, author: AuthorId | null): void;
|
|
71
|
+
private insertOp;
|
|
72
|
+
isInserted(patchId: PatchId): boolean;
|
|
73
|
+
serialize(): SerializedPatchSet;
|
|
74
|
+
}
|
|
75
|
+
export {};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { PatchId } from "@valbuild/core";
|
|
2
|
+
import type { SerializedPatchSet } from "./PatchSets.js";
|
|
3
|
+
/**
|
|
4
|
+
* Patch sets that share a change become one: a change is never split.
|
|
5
|
+
*
|
|
6
|
+
* Shared by the two places that move changes between branches a whole set at
|
|
7
|
+
* a time -- the merge moving the site's conflicting pending changes
|
|
8
|
+
* (`changesToMove` in `@valbuild/server`) and Publish selected
|
|
9
|
+
* ({@link selectionClosure}) -- so that they agree on what "a whole set" is.
|
|
10
|
+
*/
|
|
11
|
+
export declare function joinSharedChanges(sets: PatchId[][]): Set<PatchId>[];
|
|
12
|
+
/**
|
|
13
|
+
* What publishing `selected` publishes: every change in every patch set a
|
|
14
|
+
* selected change is in, sets that share a change joined
|
|
15
|
+
* (valbuild/val `docs/plans/remove-patch-groups.md`, Phase 4, rule 2).
|
|
16
|
+
*
|
|
17
|
+
* A patch set is changes that do not apply without each other, so a selection
|
|
18
|
+
* of part of one cannot be published on its own. What it pulls in is someone
|
|
19
|
+
* else's as often as not; the caller says whose, before anything moves.
|
|
20
|
+
*
|
|
21
|
+
* In the order of `patchIds` -- the chain's -- with nothing that is not in it.
|
|
22
|
+
*/
|
|
23
|
+
export declare function selectionClosure(patchSets: SerializedPatchSet, selected: Iterable<PatchId>, patchIds: readonly PatchId[]): PatchId[];
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import type { ModuleFilePath, Schema, SelectorSource, SerializedSchema } from "@valbuild/core";
|
|
2
|
+
import type { JSONValue } from "@valbuild/core/patch";
|
|
3
|
+
/**
|
|
4
|
+
* Update from the site, for a proposal with code (valbuild/home
|
|
5
|
+
* `docs/hosted-publishing.md` step 8.2, slice 1b): what content hands over,
|
|
6
|
+
* what the builder tab makes of it, and what Val's server reads -- one
|
|
7
|
+
* definition of each for the three.
|
|
8
|
+
*/
|
|
9
|
+
/** Content a schema does not allow, by path in the module. */
|
|
10
|
+
export type SchemaError = {
|
|
11
|
+
path: string[];
|
|
12
|
+
message: string;
|
|
13
|
+
};
|
|
14
|
+
/** Content's `merge-trees` answer. */
|
|
15
|
+
export type MergeTrees = {
|
|
16
|
+
base: string | null;
|
|
17
|
+
siteHead: string | null;
|
|
18
|
+
proposalHead: string | null;
|
|
19
|
+
upToDate: boolean;
|
|
20
|
+
builds: {
|
|
21
|
+
base: string;
|
|
22
|
+
site: string;
|
|
23
|
+
};
|
|
24
|
+
trees: {
|
|
25
|
+
site: Record<string, string>;
|
|
26
|
+
base: Record<string, string | null>;
|
|
27
|
+
proposal: Record<string, string | null>;
|
|
28
|
+
};
|
|
29
|
+
modules: Record<string, {
|
|
30
|
+
base: string | null;
|
|
31
|
+
site: string | null;
|
|
32
|
+
proposal: string | null;
|
|
33
|
+
}>;
|
|
34
|
+
sources: Record<string, JSONValue>;
|
|
35
|
+
};
|
|
36
|
+
/** Which tree: where the proposal started, the site, or the proposal. */
|
|
37
|
+
export type Side = "base" | "site" | "proposal";
|
|
38
|
+
/** A module, as evaluating its tree in the tab gave it. */
|
|
39
|
+
export type EvaluatedModule = {
|
|
40
|
+
schema: SerializedSchema;
|
|
41
|
+
/** The content its file's text holds. */
|
|
42
|
+
written: JSONValue;
|
|
43
|
+
/** The errors of the module's Source under this schema. */
|
|
44
|
+
errors: SchemaError[];
|
|
45
|
+
};
|
|
46
|
+
export type Evaluated = Record<ModuleFilePath, EvaluatedModule>;
|
|
47
|
+
/** A side's files: the site's tree, the base's changes over it, the proposal's over that. */
|
|
48
|
+
export declare function sideFiles(trees: MergeTrees, side: Side): Record<string, string>;
|
|
49
|
+
/** A side's Source of a module, from content's index; `null` when it has none. */
|
|
50
|
+
export declare function sideSource(trees: MergeTrees, side: Side, moduleFilePath: ModuleFilePath): JSONValue | null;
|
|
51
|
+
/**
|
|
52
|
+
* What a schema reports about this content, without the errors the server
|
|
53
|
+
* fixes on its own (those with `fixes`): run on the schema instance that
|
|
54
|
+
* evaluating the module made, so custom rules take part.
|
|
55
|
+
*/
|
|
56
|
+
export declare function schemaErrorsOf(schema: Schema<SelectorSource>, moduleFilePath: ModuleFilePath, source: JSONValue | undefined): SchemaError[];
|
|
@@ -383,10 +383,6 @@ export type ContentPublishApi = {
|
|
|
383
383
|
};
|
|
384
384
|
"/publish-jobs/:jobId/discard": {
|
|
385
385
|
POST: {
|
|
386
|
-
/** The forward closure, as `DELETE /patches` takes it. */
|
|
387
|
-
body: {
|
|
388
|
-
unstagePatchIds?: string[];
|
|
389
|
-
};
|
|
390
386
|
/** Changes it could not discard: another job in flight holds them. */
|
|
391
387
|
res: {
|
|
392
388
|
stillHeld: string[];
|
|
@@ -435,6 +431,16 @@ export type PublishRequestStatus = {
|
|
|
435
431
|
actions: ("try-again" | "discard" | "re-run-build")[];
|
|
436
432
|
/** The job that failed: what Discard is pressed on. */
|
|
437
433
|
job: string;
|
|
434
|
+
/**
|
|
435
|
+
* Where its changes went, on a project with proposals: off the site,
|
|
436
|
+
* into a proposal of their own, where they are fixed and published.
|
|
437
|
+
* `actions` is then empty -- that proposal's Publish is the Try again.
|
|
438
|
+
*/
|
|
439
|
+
movedTo?: {
|
|
440
|
+
proposal: string;
|
|
441
|
+
displayName: string;
|
|
442
|
+
changes: number;
|
|
443
|
+
};
|
|
438
444
|
} | {
|
|
439
445
|
kind: "cancelled";
|
|
440
446
|
} | {
|
|
@@ -59,8 +59,9 @@ export declare const SCHEMA_SOURCE_FIXES: ValidationFix[];
|
|
|
59
59
|
/** Whether an error is a marker only the whole project can answer. */
|
|
60
60
|
export declare function isSchemaSourceFixError(error: ValidationError): boolean;
|
|
61
61
|
/**
|
|
62
|
-
* The record a `keyof:check-keys` error resolves against
|
|
63
|
-
*
|
|
62
|
+
* The record a `keyof:check-keys` error resolves against -- or a gallery
|
|
63
|
+
* marker's (`image:check-gallery` and the rest): its gallery module -- or null
|
|
64
|
+
* for any other error, including a malformed marker, which
|
|
64
65
|
* {@link resolveSchemaSourceFixForError} reports and this has nothing to say
|
|
65
66
|
* about.
|
|
66
67
|
*
|
|
@@ -6,14 +6,16 @@
|
|
|
6
6
|
* byte-identical each time, because the query and the session are fixed for the
|
|
7
7
|
* length of a request. This is what makes the second and third call free.
|
|
8
8
|
*
|
|
9
|
-
* ## Why the scope is the REQUEST
|
|
10
|
-
*
|
|
11
|
-
* The memoised response is
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
9
|
+
* ## Why the scope is the REQUEST
|
|
10
|
+
*
|
|
11
|
+
* The memoised response is a DRAFT -- unpublished work, read with the caller's
|
|
12
|
+
* session cookie. Every caller allowed to read drafts gets the same draft (patch
|
|
13
|
+
* groups, which made it per author, are gone), but whether a caller is allowed
|
|
14
|
+
* at all is decided per session. A memo that outlived the request would hand
|
|
15
|
+
* the draft to the next visitor without asking, and it would be invisible: the
|
|
16
|
+
* page would simply render somebody's half-finished sentence. Sharing it across
|
|
17
|
+
* requests is possible now, keyed on the chain's head, and would be its own
|
|
18
|
+
* measured change.
|
|
17
19
|
*
|
|
18
20
|
* So this module holds NO state of its own. The caller supplies the box, and
|
|
19
21
|
* the box has to come from something the framework already scopes to the
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Json } from "@valbuild/core";
|
|
2
|
-
export declare const VAL_SESSION_COOKIE:
|
|
3
|
-
export declare const VAL_STATE_COOKIE:
|
|
4
|
-
export declare const VAL_ENABLE_COOKIE_NAME:
|
|
2
|
+
export declare const VAL_SESSION_COOKIE: any;
|
|
3
|
+
export declare const VAL_STATE_COOKIE: any;
|
|
4
|
+
export declare const VAL_ENABLE_COOKIE_NAME: any;
|
|
5
5
|
export type VAL_SESSION_COOKIE = typeof VAL_SESSION_COOKIE;
|
|
6
6
|
export type VAL_STATE_COOKIE = typeof VAL_STATE_COOKIE;
|
|
7
7
|
export type VAL_ENABLE_COOKIE_NAME = typeof VAL_ENABLE_COOKIE_NAME;
|
|
@@ -65,8 +65,8 @@ export declare const BinaryFileRef: z.ZodObject<{
|
|
|
65
65
|
export type BinaryFileRef = z.infer<typeof BinaryFileRef>;
|
|
66
66
|
export declare const HistoricalModule: z.ZodObject<{
|
|
67
67
|
source: z.ZodNullable<z.ZodType<JSONValueT, unknown, z.core.$ZodTypeInternals<JSONValueT, unknown>>>;
|
|
68
|
-
schema: z.ZodNullable<z.ZodType<
|
|
69
|
-
patchIds: z.ZodArray<z.ZodString & z.ZodType<
|
|
68
|
+
schema: z.ZodNullable<z.ZodType<SerializedSchemaT, unknown, z.core.$ZodTypeInternals<SerializedSchemaT, unknown>>>;
|
|
69
|
+
patchIds: z.ZodArray<z.ZodString & z.ZodType<PatchIdT, string, z.core.$ZodTypeInternals<PatchIdT, string>>>;
|
|
70
70
|
changedPaths: z.ZodArray<z.ZodString>;
|
|
71
71
|
failures: z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
72
72
|
kind: z.ZodLiteral<"commit-not-found">;
|
|
@@ -110,8 +110,8 @@ export declare const HistoricalPatchSet: z.ZodObject<{
|
|
|
110
110
|
}, z.core.$strip>;
|
|
111
111
|
modules: z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
112
112
|
source: z.ZodNullable<z.ZodType<JSONValueT, unknown, z.core.$ZodTypeInternals<JSONValueT, unknown>>>;
|
|
113
|
-
schema: z.ZodNullable<z.ZodType<
|
|
114
|
-
patchIds: z.ZodArray<z.ZodString & z.ZodType<
|
|
113
|
+
schema: z.ZodNullable<z.ZodType<SerializedSchemaT, unknown, z.core.$ZodTypeInternals<SerializedSchemaT, unknown>>>;
|
|
114
|
+
patchIds: z.ZodArray<z.ZodString & z.ZodType<PatchIdT, string, z.core.$ZodTypeInternals<PatchIdT, string>>>;
|
|
115
115
|
changedPaths: z.ZodArray<z.ZodString>;
|
|
116
116
|
failures: z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
117
117
|
kind: z.ZodLiteral<"commit-not-found">;
|
|
@@ -139,7 +139,7 @@ export declare const HistoricalPatchSet: z.ZodObject<{
|
|
|
139
139
|
}, z.core.$strip>], "kind">>;
|
|
140
140
|
}, z.core.$strip>>;
|
|
141
141
|
patches: z.ZodArray<z.ZodObject<{
|
|
142
|
-
patchId: z.ZodString & z.ZodType<
|
|
142
|
+
patchId: z.ZodString & z.ZodType<PatchIdT, string, z.core.$ZodTypeInternals<PatchIdT, string>>;
|
|
143
143
|
moduleFilePath: z.ZodString;
|
|
144
144
|
patch: z.ZodUnknown;
|
|
145
145
|
authorId: z.ZodNullable<z.ZodString>;
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import type { JSONValue as JSONValueT, PatchJSON as PatchJSONT, PatchBlock as PatchBlockT, ParentRef as ParentRefT } from "@valbuild/core/patch";
|
|
2
|
-
import type { PatchId as PatchIdT } from "@valbuild/core";
|
|
3
2
|
import { z } from "zod";
|
|
4
3
|
/**
|
|
5
4
|
* JSON, recursively — the shape of a Source over the wire.
|
|
@@ -83,7 +83,7 @@ export declare const SerializedSettingsSchema: z.ZodType<SerializedSettingsSchem
|
|
|
83
83
|
*/
|
|
84
84
|
export declare const SerializedValViewSchema: z.ZodObject<{
|
|
85
85
|
type: z.ZodLiteral<"view">;
|
|
86
|
-
moduleFilePath: z.ZodType<
|
|
86
|
+
moduleFilePath: z.ZodType<ModuleFilePathT, unknown, z.core.$ZodTypeInternals<ModuleFilePathT, unknown>>;
|
|
87
87
|
opt: z.ZodLiteral<false>;
|
|
88
88
|
readonly: z.ZodOptional<z.ZodBoolean>;
|
|
89
89
|
hidden: z.ZodOptional<z.ZodBoolean>;
|