@valbuild/shared 0.123.2 → 0.124.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/CHANGELOG.md CHANGED
@@ -1,5 +1,113 @@
1
1
  # @valbuild/shared
2
2
 
3
+ ## 0.124.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#639](https://github.com/valbuild/val/pull/639) [`ad7fff4`](https://github.com/valbuild/val/commit/ad7fff4cc8cea98c506cf7ff9b4e8d6e5ffa4055) Thanks [@freekh](https://github.com/freekh)! - Show `.jsonValues()` entries in the history pane
8
+
9
+ A `.jsonValues()` record keeps each entry's content in its own `*.val.json`
10
+ file; the module's own content is just markers pointing at them. The history
11
+ pane had no way to fetch those files for a past commit, so every entry rendered
12
+ as an **empty field** — which reads as "the author left this blank", about
13
+ content that was simply stored somewhere else.
14
+
15
+ Entries now load in the history pane the same way they load in the Studio: one
16
+ at a time, when you open one. A commit with a thousand support pages costs
17
+ nothing until you look at one of them.
18
+
19
+ An entry that cannot be read says so instead of rendering blank — including the
20
+ case where the key did not exist yet at that commit, which is a real answer
21
+ rather than an error.
22
+
23
+ ### Patch Changes
24
+
25
+ - [#639](https://github.com/valbuild/val/pull/639) [`ad7fff4`](https://github.com/valbuild/val/commit/ad7fff4cc8cea98c506cf7ff9b4e8d6e5ffa4055) Thanks [@freekh](https://github.com/freekh)! - Require a session to read a file from history
26
+
27
+ `GET /api/val/history/files` served a file at a given commit to anyone who
28
+ asked. It followed the reasoning of `/api/val/files`, which is deliberately
29
+ open — and neither half of that reasoning applies to it:
30
+
31
+ - `/files` stands on `patch_id` being an unguessable UUID. A **commit sha is
32
+ published** — `git log`, the GitHub UI, every pull request — so it is no
33
+ substitute for a credential.
34
+ - `/files` also _cannot_ require auth: draft images are fetched by the app's own
35
+ backend during Next image optimisation, with no cookies to send. Nothing
36
+ fetches history files server-side; both callers are the Studio, in a browser
37
+ that already holds the session.
38
+
39
+ So history files now require a session. `/files` is unchanged, and the reason
40
+ the two differ is written down in `architecture/media.md` so it is not "fixed"
41
+ in either direction later.
42
+
43
+ No action needed: the Studio sends its session cookie automatically.
44
+
45
+ - Updated dependencies [[`aa89fc2`](https://github.com/valbuild/val/commit/aa89fc26de7028b3c5a1666b99afc03b39e92769)]:
46
+ - @valbuild/core@0.124.0
47
+
48
+ ## 0.123.3
49
+
50
+ ### Patch Changes
51
+
52
+ - [#633](https://github.com/valbuild/val/pull/633) [`accf4f8`](https://github.com/valbuild/val/commit/accf4f852fe3400d762cc14e80316da741a69a9a) Thanks [@freekh](https://github.com/freekh)! - Stop `@valbuild/shared` replacing a consumer's own flexsearch types
53
+
54
+ `@valbuild/shared` gained a flexsearch dependency in 0.123.0, for
55
+ `search_content`, and typed its exported `SearchIndex.index` as flexsearch's
56
+ own `Index`. That put `import { Index } from "flexsearch"` into the published
57
+ `searchIndex.d.ts` — and flexsearch's type entry opens with
58
+ `declare module "flexsearch"`, which is an AMBIENT module declaration and so
59
+ global to a whole TypeScript program.
60
+
61
+ The effect on a project that uses flexsearch itself, at a different version:
62
+ its own calls are checked against the copy Val dragged in. valbuild/web pins
63
+ flexsearch 0.7 and builds an index with
64
+ `new flexsearch.Document({ language: "en", … })`; on `@valbuild/shared@0.123.2`
65
+ that stopped compiling, because 0.8's `DocumentOptions` has no `language`. Its
66
+ resolved copy was still 0.7 — only the types had been swapped underneath it.
67
+
68
+ `SearchIndex.index` is now a structural type covering the three methods this
69
+ package calls, so nothing in the published declarations names flexsearch. The
70
+ library is still used to build the index; that is a value import, which never
71
+ reaches a `.d.ts`.
72
+
73
+ No API change: `SearchIndex` is still assignable from a real flexsearch
74
+ `Index`, and `noFlexsearchInPublishedTypes.test.ts` asserts both halves of that
75
+ — that no source file names flexsearch in a type position, and that a real
76
+ `Index` still satisfies the stand-in, so it cannot drift from the library
77
+ unnoticed.
78
+
79
+ - [#628](https://github.com/valbuild/val/pull/628) [`aef45ce`](https://github.com/valbuild/val/commit/aef45ce3ef269f1b6221de44a56df0f6a0d9dbbd) Thanks [@freekh](https://github.com/freekh)! - Stop the serialized-schema parser dropping schema metadata
80
+
81
+ `SerializedSchema` in `@valbuild/shared` is a zod mirror of the serialized
82
+ schema type, and its `z.object`s strip keys they do not declare. Five fields
83
+ were declared on some schemas and forgotten on others:
84
+
85
+ - `customValidate` — missing on twelve of the eighteen schemas. This is the flag
86
+ that says a schema declares a `.validate()`; the function itself cannot
87
+ serialize, so the flag is the only thing that tells the Studio to run the
88
+ custom validators against the real instance.
89
+ - `description` — missing on thirteen, so a `.describe()` went unseen.
90
+ - `remote` and `referencedModule` — missing on `s.image()` and `s.file()`, so a
91
+ remote field read as local and a gallery-backed field lost the gallery it
92
+ reads its dimensions and mime type from.
93
+ - the `message` on a `.regexp(pattern, message)` — so a field with a custom
94
+ pattern message fell back to the generic "Expected string to match reg
95
+ exp: …".
96
+
97
+ Because it strips rather than rejects, nothing failed and nothing said so. The
98
+ live `/schema` route was unaffected — `ValClient` validates the response and
99
+ then returns the raw JSON — but the history path consumes the parsed output, so
100
+ in a commit or historical patch-set view (`getModuleAtCommit`,
101
+ `getHistoricalPatchSet`) a schema's custom validators, description, remoteness
102
+ and backing gallery all quietly disappeared.
103
+
104
+ The fields every serialized schema shares are now spread from one
105
+ `commonSchemaFields` object instead of being retyped eighteen times, which is
106
+ how they drifted apart in the first place. A round-trip test walks each schema's
107
+ serialized output against the parsed result and fails with the path of any key
108
+ that did not survive, so a field added to a serialized schema and forgotten in
109
+ the parser is caught without anyone having to remember to assert it.
110
+
3
111
  ## 0.123.2
4
112
 
5
113
  ### Patch Changes
@@ -1329,6 +1329,80 @@ export declare const Api: {
1329
1329
  * actually mounts. A file at a fixed commit cannot change, so the response is
1330
1330
  * immutable and the browser's own cache handles flipping between commits.
1331
1331
  */
1332
+ /**
1333
+ * One `.jsonValues()` entry's content, as it was at a commit.
1334
+ *
1335
+ * The history pane's counterpart to `/json`. A `.jsonValues()` record's
1336
+ * source is only `{_type:"json"}` markers - the content is per entry, fetched
1337
+ * on demand - so without this the commit pane rendered every entry as an
1338
+ * empty field, which is a claim that the author left it blank.
1339
+ *
1340
+ * One entry per request, deliberately: the store fetches an entry when
1341
+ * something reads inside it, so a batch would mean guessing which entries the
1342
+ * pane is about to show. Authenticated for the same reasons as
1343
+ * `/history/files` below.
1344
+ */
1345
+ "/history/json": {
1346
+ GET: {
1347
+ req: {
1348
+ query: {
1349
+ commit_sha: z.ZodPipe<z.ZodArray<z.ZodString>, z.ZodTransform<string, string[]>>;
1350
+ path: z.ZodPipe<z.ZodArray<z.ZodString>, z.ZodTransform<string, string[]>>;
1351
+ key: z.ZodPipe<z.ZodArray<z.ZodString>, z.ZodTransform<string, string[]>>;
1352
+ };
1353
+ cookies: {
1354
+ val_session: z.ZodOptional<z.ZodString>;
1355
+ };
1356
+ };
1357
+ res: z.ZodUnion<readonly [z.ZodObject<{
1358
+ status: z.ZodLiteral<401>;
1359
+ json: z.ZodObject<{
1360
+ message: z.ZodString;
1361
+ }, z.core.$strip>;
1362
+ }, z.core.$strip>, z.ZodObject<{
1363
+ status: z.ZodLiteral<404>;
1364
+ json: z.ZodObject<{
1365
+ message: z.ZodString;
1366
+ }, z.core.$strip>;
1367
+ }, z.core.$strip>, z.ZodObject<{
1368
+ status: z.ZodLiteral<400>;
1369
+ json: z.ZodIntersection<z.ZodObject<{
1370
+ message: z.ZodString;
1371
+ }, z.core.$strip>, z.ZodObject<{
1372
+ kind: z.ZodOptional<z.ZodString>;
1373
+ }, z.core.$strip>>;
1374
+ }, z.core.$strip>, z.ZodObject<{
1375
+ status: z.ZodLiteral<500>;
1376
+ json: z.ZodIntersection<z.ZodObject<{
1377
+ message: z.ZodString;
1378
+ }, z.core.$strip>, z.ZodObject<{
1379
+ kind: z.ZodOptional<z.ZodString>;
1380
+ }, z.core.$strip>>;
1381
+ }, z.core.$strip>, z.ZodObject<{
1382
+ status: z.ZodLiteral<200>;
1383
+ json: z.ZodObject<{
1384
+ path: z.ZodString & z.ZodType<ModuleFilePath, string, z.core.$ZodTypeInternals<ModuleFilePath, string>>;
1385
+ key: z.ZodString;
1386
+ content: z.ZodUnknown;
1387
+ }, z.core.$strip>;
1388
+ }, z.core.$strip>]>;
1389
+ };
1390
+ };
1391
+ /**
1392
+ * A binary file as it was at a commit.
1393
+ *
1394
+ * AUTHENTICATED, unlike `/files` below - and the difference is not an
1395
+ * oversight in either direction. See the comment on `/files`: what makes it
1396
+ * safe to leave open is that a `patch_id` is an unguessable UUID, and what
1397
+ * makes leaving it open NECESSARY is that a draft image is fetched by the
1398
+ * app's own backend during Next image optimisation, with no cookies.
1399
+ *
1400
+ * Neither holds here. A commit sha is published - `git log`, the GitHub UI,
1401
+ * every PR - so it is not a secret to stand in for a credential. And nothing
1402
+ * fetches these server-side: they are read by the Studio, in a browser that
1403
+ * has the session cookie, for the history pane and for staging a restore.
1404
+ * So this one asks.
1405
+ */
1332
1406
  "/history/files": {
1333
1407
  GET: {
1334
1408
  req: {
@@ -1337,6 +1411,9 @@ export declare const Api: {
1337
1411
  path: z.ZodPipe<z.ZodArray<z.ZodString>, z.ZodTransform<string, string[]>>;
1338
1412
  remote: z.ZodOptional<z.ZodPipe<z.ZodArray<z.ZodString>, z.ZodTransform<string, string[]>>>;
1339
1413
  };
1414
+ cookies: {
1415
+ val_session: z.ZodOptional<z.ZodString>;
1416
+ };
1340
1417
  };
1341
1418
  res: z.ZodUnion<readonly [z.ZodObject<{
1342
1419
  status: z.ZodLiteral<401>;
@@ -1,5 +1,4 @@
1
1
  import { Json, ModuleFilePath, SerializedSchema, SourcePath } from "@valbuild/core";
2
- import { Index } from "flexsearch";
3
2
  /**
4
3
  * The search index and the labels to render for its hits.
5
4
  *
@@ -15,8 +14,37 @@ import { Index } from "flexsearch";
15
14
  * them — an agent and an editor searching the same project should find the same
16
15
  * things.
17
16
  */
17
+ /**
18
+ * The parts of FlexSearch's `Index` this module uses.
19
+ *
20
+ * Structural, rather than importing flexsearch's own `Index`, because
21
+ * {@link SearchIndex} is EXPORTED — and a `.d.ts` that names `flexsearch` pulls
22
+ * that package's `declare module "flexsearch"` into every consumer's TypeScript
23
+ * program. Ambient module declarations are global to a program, so a consumer
24
+ * with its own flexsearch at another version then checks its OWN calls against
25
+ * the copy we dragged in, whichever of the two wins.
26
+ *
27
+ * That is not hypothetical: `@valbuild/shared` gained a flexsearch dependency
28
+ * in 0.123.0 for `search_content`, and valbuild/web — which pins flexsearch
29
+ * 0.7 and calls `new flexsearch.Document({ language: "en", … })` — stopped
30
+ * building on 0.123.2, because 0.8's `DocumentOptions` has no `language`. Its
31
+ * own resolved copy was still 0.7; only the types had been replaced.
32
+ *
33
+ * Three methods is all this module calls, and none of their signatures here is
34
+ * a guess: `add` and `remove` are used in `indexModule`/`removeModule`, and
35
+ * `search`'s result is only ever `.length`-ed, `.slice`-d and mapped over as
36
+ * ids. `FlexSearch.Index` is still constructed with the real library in
37
+ * {@link createSearchIndex} — a value import, which does not reach the `.d.ts`.
38
+ */
39
+ type SearchableIndex = {
40
+ add(id: string, content: string): void;
41
+ remove(id: string): void;
42
+ search(query: string, options: {
43
+ limit: number;
44
+ }): (string | number)[];
45
+ };
18
46
  export type SearchIndex = {
19
- index: Index;
47
+ index: SearchableIndex;
20
48
  pathToLabel: Map<string, string>;
21
49
  /**
22
50
  * Which document ids belong to which module, so one module can be re-indexed
@@ -74,3 +102,4 @@ export type SearchResults = {
74
102
  totalIsLowerBound: boolean;
75
103
  };
76
104
  export declare function performSearch(searchIndex: SearchIndex | null, query: string, limit?: number, offset?: number): SearchResults;
105
+ export {};