@valbuild/shared 0.150.20 → 0.152.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 +268 -0
- package/client/dist/valbuild-shared-client.cjs.dev.js +1 -1
- package/client/dist/valbuild-shared-client.cjs.prod.js +1 -1
- package/client/dist/valbuild-shared-client.esm.js +1 -1
- package/dist/declarations/src/internal/ApiRoutes.d.ts +270 -2
- package/dist/declarations/src/internal/aiTools/toolNames.d.ts +1 -1
- package/dist/declarations/src/internal/content/applyChain.d.ts +124 -0
- package/dist/declarations/src/internal/content/buildRegistry.d.ts +36 -0
- package/dist/declarations/src/internal/content/jsonValuesOps.d.ts +110 -0
- package/dist/declarations/src/internal/content/moduleStore.d.ts +119 -0
- package/dist/declarations/src/internal/content/source.d.ts +71 -0
- package/dist/declarations/src/internal/content/version.d.ts +151 -0
- package/dist/declarations/src/internal/index.d.ts +8 -1
- package/dist/declarations/src/internal/publish/projectSource.d.ts +28 -0
- package/dist/declarations/src/internal/search/searchIndex.d.ts +34 -3
- package/dist/declarations/src/internal/server/types.d.ts +9 -1
- package/dist/declarations/src/internal/sketchpad/sketchFormat.d.ts +41 -6
- package/dist/declarations/src/internal/valCanvasProtocol.d.ts +46 -0
- package/dist/declarations/src/internal/valClientHeader.d.ts +36 -0
- package/dist/declarations/src/publish/callJson.d.ts +15 -0
- package/dist/declarations/src/publish/jobClient.d.ts +7 -1
- package/dist/declarations/src/publish/publishClient.d.ts +2 -14
- package/dist/{getSitemapTree-fef55872.esm.js → getSitemapTree-3c1b9e57.esm.js} +18 -2
- package/dist/{getSitemapTree-6dfa2f4e.cjs.dev.js → getSitemapTree-59c5ed5d.cjs.dev.js} +18 -2
- package/dist/{getSitemapTree-8a04f3cf.cjs.prod.js → getSitemapTree-6b9024d1.cjs.prod.js} +18 -2
- package/dist/{protocol-d0efb8cd.cjs.dev.js → projectSource-2a58ef15.cjs.dev.js} +221 -0
- package/dist/{protocol-55cfa3cf.esm.js → projectSource-7f0ee83c.esm.js} +216 -1
- package/dist/{protocol-b1055ef0.cjs.prod.js → projectSource-ed6aec39.cjs.prod.js} +221 -0
- package/internal/dist/valbuild-shared-internal.cjs.dev.js +2521 -192
- package/internal/dist/valbuild-shared-internal.cjs.prod.js +2521 -192
- package/internal/dist/valbuild-shared-internal.esm.js +2463 -175
- package/package.json +3 -3
- package/publish/dist/valbuild-shared-publish.cjs.dev.js +48 -155
- package/publish/dist/valbuild-shared-publish.cjs.prod.js +48 -155
- package/publish/dist/valbuild-shared-publish.esm.js +17 -123
- package/dist/declarations/src/internal/server/requestScopedMemo.d.ts +0 -49
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import { f as _createForOfIteratorHelper, d as _objectSpread2, e as _toConsumableArray, _ as _typeof, g as _defineProperty, b as _asyncToGenerator, c as _regenerator, a as _slicedToArray } from '../../dist/createForOfIteratorHelper-9b80f1a2.esm.js';
|
|
2
|
-
import { Internal, CODE_LANGUAGES, resolveSettingsModule, missingDeclaredKeys, localeOfValue, routePatternOf, matchRoutePattern, discriminatedUnionBranchOf, DEFAULT_COLOR_FORMAT, declaredKeySetOf } from '@valbuild/core';
|
|
2
|
+
import { Internal, CODE_LANGUAGES, VAL_EXTENSION, resolveSettingsModule, missingDeclaredKeys, localeOfValue, routePatternOf, matchRoutePattern, discriminatedUnionBranchOf, DEFAULT_COLOR_FORMAT, declaredKeySetOf } from '@valbuild/core';
|
|
3
3
|
import { z } from 'zod';
|
|
4
|
-
import { _ as _objectWithoutProperties } from '../../dist/
|
|
5
|
-
export { P as PublishProtocolError,
|
|
4
|
+
import { _ as _objectWithoutProperties } from '../../dist/projectSource-7f0ee83c.esm.js';
|
|
5
|
+
export { P as PublishProtocolError, v as createProjectSourceReader, c as parseArtifacts, a as parseBuildTarget, n as parseCancel, b as parseDeclare, o as parseDiscard, l as parseJob, q as parseNewestCiRun, j as parsePress, i as parseProblems, h as parseProjectSource, e as parsePromote, t as parsePublishToken, m as parseRenew, k as parseRequestStatus, f as parseStatus, u as parseUpdateTarget, d as parseVerify, p as publishApiOf } from '../../dist/projectSource-7f0ee83c.esm.js';
|
|
6
|
+
import { a as _createClass, c as _classCallCheck } from '../../dist/wrapNativeSuper-639b12b7.esm.js';
|
|
6
7
|
import { fromError } from 'zod-validation-error';
|
|
7
|
-
|
|
8
|
+
import { result, array } from '@valbuild/core/fp';
|
|
9
|
+
import { PatchError, deepEqual, parseAndValidateArrayIndex } from '@valbuild/core/patch';
|
|
10
|
+
export { D as DEFAULT_CANVAS_SELECTION, e as DEFAULT_CANVAS_SELECTION_SOFT, Q as QUICK_PREVIEW_PARAM, v as QUICK_PREVIEW_RENDER_SCRIPT, u as QUICK_PREVIEW_SERVICE_WORKER_PATH, z as QUICK_PREVIEW_SERVICE_WORKER_SOURCE, x as QUICK_PREVIEW_STORE_MESSAGE, b as VAL_AI_SESSION_STORAGE_KEY, d as VAL_CANVAS_MESSAGE, c as VAL_CANVAS_PARAM, V as VAL_CONFIG_SESSION_STORAGE_KEY, a as VAL_THEME_SESSION_STORAGE_KEY, j as attachElementPicker, k as createPickerOverlay, h as describeElement, l as elementLabel, q as findBySelector, G as formatRoutePattern, H as getNextAppRouterSourceFolder, L as getPageRouterSitemapTree, J as getPageRouterSourceFolder, M as getPatternFromModuleFilePath, I as getTanStackRouterSourceFolder, E as isDesignTabOrigin, y as isQuickPreviewFiles, K as isTanStackRoutesFolder, g as isValCanvasFrame, i as isValCanvasPageMessage, f as isValCanvasStudioMessage, m as modifierLabel, p as pageLocationOf, F as parseRoutePattern, C as quickPreviewSiteOrigin, o as readPageSnapshot, r as restoreScroll, t as screenshotPage, s as startElementPicking, A as storeQuickPreview, n as watchPageLocation, B as withQuickPreviewParam, w as withValCanvasParam } from '../../dist/getSitemapTree-3c1b9e57.esm.js';
|
|
8
11
|
import FlexSearch from 'flexsearch';
|
|
9
|
-
import { a as _createClass, c as _classCallCheck } from '../../dist/wrapNativeSuper-639b12b7.esm.js';
|
|
10
12
|
import { b as base64DataUrlToUint8Array } from '../../dist/utils-00f3e1df.esm.js';
|
|
11
13
|
|
|
12
14
|
function remirrorToRichTextSource(node, configDirectory, remoteOptions) {
|
|
@@ -197,7 +199,7 @@ var convertToNumber = function convertToNumber(value) {
|
|
|
197
199
|
var parsedValue = parseInt(value, 10);
|
|
198
200
|
return isNaN(parsedValue) ? 0 : parsedValue;
|
|
199
201
|
};
|
|
200
|
-
var textEncoder = new TextEncoder();
|
|
202
|
+
var textEncoder$1 = new TextEncoder();
|
|
201
203
|
function convertImageNode(path, node, files, configDirectory, remoteOptions) {
|
|
202
204
|
if (node.attrs && node.attrs.src.startsWith("data:")) {
|
|
203
205
|
var binaryData = base64DataUrlToUint8Array(node.attrs.src);
|
|
@@ -223,7 +225,7 @@ function convertImageNode(path, node, files, configDirectory, remoteOptions) {
|
|
|
223
225
|
validationHash: Internal.remote.getValidationHash(remoteOptions.coreVersion, remoteOptions.schema, mimeType, {
|
|
224
226
|
width: typeof node.attrs.width === "number" ? node.attrs.width : 0,
|
|
225
227
|
height: typeof node.attrs.height === "number" ? node.attrs.height : 0
|
|
226
|
-
}, remoteFileHash, textEncoder),
|
|
228
|
+
}, remoteFileHash, textEncoder$1),
|
|
227
229
|
filePath: filePath.slice(1)
|
|
228
230
|
})) : filePath;
|
|
229
231
|
if (existingFilesEntry) {
|
|
@@ -741,62 +743,9 @@ var VAL_STATE_COOKIE = Internal.VAL_STATE_COOKIE;
|
|
|
741
743
|
var VAL_ENABLE_COOKIE_NAME = Internal.VAL_ENABLE_COOKIE_NAME;
|
|
742
744
|
|
|
743
745
|
/**
|
|
744
|
-
*
|
|
745
|
-
*
|
|
746
|
-
* `fetchVal` in draft mode reads the whole module tree out of `/sources/~`, and
|
|
747
|
-
* a page that calls it three times used to ask three times — the answer being
|
|
748
|
-
* byte-identical each time, because the query and the session are fixed for the
|
|
749
|
-
* length of a request. This is what makes the second and third call free.
|
|
750
|
-
*
|
|
751
|
-
* ## Why the scope is the REQUEST
|
|
752
|
-
*
|
|
753
|
-
* The memoised response is a DRAFT -- unpublished work, read with the caller's
|
|
754
|
-
* session cookie. Every caller allowed to read drafts gets the same draft (patch
|
|
755
|
-
* groups, which made it per author, are gone), but whether a caller is allowed
|
|
756
|
-
* at all is decided per session. A memo that outlived the request would hand
|
|
757
|
-
* the draft to the next visitor without asking, and it would be invisible: the
|
|
758
|
-
* page would simply render somebody's half-finished sentence. Sharing it across
|
|
759
|
-
* requests is possible now, keyed on the chain's head, and would be its own
|
|
760
|
-
* measured change.
|
|
761
|
-
*
|
|
762
|
-
* So this module holds NO state of its own. The caller supplies the box, and
|
|
763
|
-
* the box has to come from something the framework already scopes to the
|
|
764
|
-
* request — React's `cache()` in an RSC, a `WeakMap` keyed on the `Request` in
|
|
765
|
-
* TanStack Start. `null` means "no request scope available", and that is not an
|
|
766
|
-
* error: it falls back to computing every time, which is what the code did
|
|
767
|
-
* before this existed. Ineffective is a safe failure here; leaking is not.
|
|
768
|
-
*
|
|
769
|
-
* The `key` is the second half of that guarantee. It carries whatever the
|
|
770
|
-
* answer depends on — the session — so a box that somehow outlived its request,
|
|
771
|
-
* or is shared by two sessions, MISSES rather than serving the wrong draft.
|
|
772
|
-
*
|
|
773
|
-
* ## The promise is cached, not the value
|
|
774
|
-
*
|
|
775
|
-
* React renders siblings concurrently, so three `fetchVal` calls in one page
|
|
776
|
-
* are usually in flight at the same time rather than one after another.
|
|
777
|
-
* Caching the resolved value would dedupe none of them; caching the promise
|
|
778
|
-
* dedupes both orderings.
|
|
779
|
-
*/
|
|
780
|
-
|
|
781
|
-
/**
|
|
782
|
-
* Returns the memoised promise for `key`, computing it only on a miss.
|
|
783
|
-
*
|
|
784
|
-
* A rejection is memoised along with everything else, on purpose: one failed
|
|
785
|
-
* read per request is the same answer for every caller in it, and retrying it
|
|
786
|
-
* two more times only makes a broken draft slower to render.
|
|
746
|
+
* Nothing moved since the ETag the caller sent back: no body, as HTTP
|
|
747
|
+
* requires of a 304.
|
|
787
748
|
*/
|
|
788
|
-
function memoizePerRequest(box, key, compute) {
|
|
789
|
-
if (box === null) {
|
|
790
|
-
return compute();
|
|
791
|
-
}
|
|
792
|
-
if (box.promise !== undefined && box.key === key) {
|
|
793
|
-
return box.promise;
|
|
794
|
-
}
|
|
795
|
-
var promise = compute();
|
|
796
|
-
box.key = key;
|
|
797
|
-
box.promise = promise;
|
|
798
|
-
return promise;
|
|
799
|
-
}
|
|
800
749
|
|
|
801
750
|
/**
|
|
802
751
|
* JSON, recursively — the shape of a Source over the wire.
|
|
@@ -1302,6 +1251,228 @@ var ValDeployment = z.object({
|
|
|
1302
1251
|
commitMessage: z.string().nullable().optional()
|
|
1303
1252
|
});
|
|
1304
1253
|
|
|
1254
|
+
var _Internal$VERSION$cor;
|
|
1255
|
+
|
|
1256
|
+
/**
|
|
1257
|
+
* What a module's content IS: the schema it is read and patched with, the
|
|
1258
|
+
* base it starts from, and the chain of patches on top. Three parts because
|
|
1259
|
+
* each kind of change moves exactly one of them (see
|
|
1260
|
+
* `docs/plans/one-content-model.md`, "A version, per module, with the code in
|
|
1261
|
+
* it"):
|
|
1262
|
+
*
|
|
1263
|
+
* - a schema change, or a new engine (`@valbuild/*` upgraded), moves `schema`;
|
|
1264
|
+
* - a publish, a content version, a literal edited in code, moves `base`;
|
|
1265
|
+
* - an edit, a discard, a publish (which empties the chain) moves `chain`.
|
|
1266
|
+
*
|
|
1267
|
+
* Two answers with the same version are the same content, wherever they came
|
|
1268
|
+
* from. That is the whole point: comparing contents becomes comparing three
|
|
1269
|
+
* strings.
|
|
1270
|
+
*
|
|
1271
|
+
* Computed when content CHANGES -- a module evaluated, a base adopted, a patch
|
|
1272
|
+
* appended -- never when it is read (risk 3 in the plan): every function here
|
|
1273
|
+
* that hashes counts itself ({@link versionHashCount}), so a reader that
|
|
1274
|
+
* hashes is caught by a counted budget rather than by a profile.
|
|
1275
|
+
*/
|
|
1276
|
+
|
|
1277
|
+
/**
|
|
1278
|
+
* What a project answer is relative to: which build served it, and how far
|
|
1279
|
+
* along the project's changes it is.
|
|
1280
|
+
*/
|
|
1281
|
+
|
|
1282
|
+
/**
|
|
1283
|
+
* What the schema version of a module staged after the build serving a
|
|
1284
|
+
* proposal starts with: `staged:<inputs>`. Val's server has not got that
|
|
1285
|
+
* module's schema, so it names the staging instead; a Studio compares no
|
|
1286
|
+
* build's code against it.
|
|
1287
|
+
*/
|
|
1288
|
+
var STAGED_SCHEMA_PREFIX = "staged:";
|
|
1289
|
+
|
|
1290
|
+
/** The chain of no patches. */
|
|
1291
|
+
var EMPTY_CHAIN = "";
|
|
1292
|
+
|
|
1293
|
+
/**
|
|
1294
|
+
* The engine: how patches are applied and content is read. Part of every
|
|
1295
|
+
* schema version, because content read by another engine is not the same
|
|
1296
|
+
* content even under the same schema.
|
|
1297
|
+
*/
|
|
1298
|
+
var ENGINE = "@valbuild/core@".concat((_Internal$VERSION$cor = Internal.VERSION.core) !== null && _Internal$VERSION$cor !== void 0 ? _Internal$VERSION$cor : "unknown");
|
|
1299
|
+
var hashes = 0;
|
|
1300
|
+
var textEncoder;
|
|
1301
|
+
function sha256Hex(input) {
|
|
1302
|
+
hashes++;
|
|
1303
|
+
textEncoder !== null && textEncoder !== void 0 ? textEncoder : textEncoder = new TextEncoder();
|
|
1304
|
+
return Internal.getSHA256Hash(textEncoder.encode(input));
|
|
1305
|
+
}
|
|
1306
|
+
|
|
1307
|
+
/**
|
|
1308
|
+
* How many times a version has been hashed in this process: the counted
|
|
1309
|
+
* budget behind "versions are computed when content changes, never when it
|
|
1310
|
+
* is read" (`version:hash`). A test takes it before and after a read.
|
|
1311
|
+
*/
|
|
1312
|
+
function versionHashCount() {
|
|
1313
|
+
return hashes;
|
|
1314
|
+
}
|
|
1315
|
+
|
|
1316
|
+
/** A chain one patch longer. */
|
|
1317
|
+
function extendChain(prev, id) {
|
|
1318
|
+
return sha256Hex("".concat(prev, "\n").concat(id));
|
|
1319
|
+
}
|
|
1320
|
+
|
|
1321
|
+
/** The chain of these patch ids, in order, on top of a base. */
|
|
1322
|
+
function chainOf(ids) {
|
|
1323
|
+
var chain = EMPTY_CHAIN;
|
|
1324
|
+
var _iterator = _createForOfIteratorHelper(ids),
|
|
1325
|
+
_step;
|
|
1326
|
+
try {
|
|
1327
|
+
for (_iterator.s(); !(_step = _iterator.n()).done;) {
|
|
1328
|
+
var id = _step.value;
|
|
1329
|
+
chain = extendChain(chain, id);
|
|
1330
|
+
}
|
|
1331
|
+
} catch (err) {
|
|
1332
|
+
_iterator.e(err);
|
|
1333
|
+
} finally {
|
|
1334
|
+
_iterator.f();
|
|
1335
|
+
}
|
|
1336
|
+
return chain;
|
|
1337
|
+
}
|
|
1338
|
+
|
|
1339
|
+
/**
|
|
1340
|
+
* A module's schema version, from its serialized schema. The same function
|
|
1341
|
+
* on the server (from what `extractValModules` serialized) and in the Studio
|
|
1342
|
+
* (from what it holds in `SchemaStore`), so the two compare.
|
|
1343
|
+
*/
|
|
1344
|
+
function schemaVersionOf(serializedSchema) {
|
|
1345
|
+
return sha256Hex("".concat(ENGINE, "\n").concat(JSON.stringify(serializedSchema)));
|
|
1346
|
+
}
|
|
1347
|
+
|
|
1348
|
+
/**
|
|
1349
|
+
* A module's base version, from its base source: what the module evaluates
|
|
1350
|
+
* to, or what a content version or a publish put in its place. `.jsonValues()`
|
|
1351
|
+
* entries are markers here; an entry's own version comes with entries stored
|
|
1352
|
+
* as modules of their own (phase 6).
|
|
1353
|
+
*/
|
|
1354
|
+
function baseVersionOf(source) {
|
|
1355
|
+
var _JSON$stringify;
|
|
1356
|
+
return sha256Hex((_JSON$stringify = JSON.stringify(source)) !== null && _JSON$stringify !== void 0 ? _JSON$stringify : "undefined");
|
|
1357
|
+
}
|
|
1358
|
+
|
|
1359
|
+
/** The address of a module's content. Never changes meaning. */
|
|
1360
|
+
function moduleKey(path, v) {
|
|
1361
|
+
return "".concat(path, "@").concat(v.schema, ".").concat(v.base, ".").concat(v.chain);
|
|
1362
|
+
}
|
|
1363
|
+
|
|
1364
|
+
/** Whether two versions name the same content. */
|
|
1365
|
+
function sameModuleVersion(a, b) {
|
|
1366
|
+
return a.schema === b.schema && a.base === b.base && a.chain === b.chain;
|
|
1367
|
+
}
|
|
1368
|
+
|
|
1369
|
+
/**
|
|
1370
|
+
* The chains of every module, from the patches a draft applies (in chain
|
|
1371
|
+
* order), folded incrementally.
|
|
1372
|
+
*
|
|
1373
|
+
* A read asks for the chains every time; the fold is extended only by the
|
|
1374
|
+
* patches the chain has gained since the last call, and a call that finds the
|
|
1375
|
+
* chain as it was hashes nothing. A chain that changed anywhere but at its end
|
|
1376
|
+
* (a discard in the middle, a publish) is folded again from the start --
|
|
1377
|
+
* once, when that is first noticed.
|
|
1378
|
+
*/
|
|
1379
|
+
var ChainFold = /*#__PURE__*/function () {
|
|
1380
|
+
function ChainFold() {
|
|
1381
|
+
_classCallCheck(this, ChainFold);
|
|
1382
|
+
_defineProperty(this, "ids", []);
|
|
1383
|
+
_defineProperty(this, "paths", []);
|
|
1384
|
+
_defineProperty(this, "chains", new Map());
|
|
1385
|
+
/**
|
|
1386
|
+
* Every module's chain after each of its patches, in order: what
|
|
1387
|
+
* {@link chainAt} answers for a module whose content stopped part way
|
|
1388
|
+
* along its chain (a patch that does not apply). Kept as the chains are
|
|
1389
|
+
* extended, so asking hashes nothing.
|
|
1390
|
+
*/
|
|
1391
|
+
_defineProperty(this, "prefixes", new Map());
|
|
1392
|
+
/**
|
|
1393
|
+
* Bumped whenever the chains move: a cheap identity for "the same chains as
|
|
1394
|
+
* before", in-process. With {@link instance} in front, two folds never name
|
|
1395
|
+
* one epoch: two processes (or isolates) holding the same chains answer
|
|
1396
|
+
* differently, which only costs a cache a miss, where two holding
|
|
1397
|
+
* DIFFERENT chains at the same count must never answer alike (an ETag
|
|
1398
|
+
* answered 304 for content the caller does not have).
|
|
1399
|
+
*/
|
|
1400
|
+
_defineProperty(this, "epochValue", 0);
|
|
1401
|
+
_defineProperty(this, "instance", Math.random().toString(36).slice(2, 10));
|
|
1402
|
+
}
|
|
1403
|
+
return _createClass(ChainFold, [{
|
|
1404
|
+
key: "chainsOf",
|
|
1405
|
+
value: /** Every module's chain, for these patches. Modules with none are absent. */
|
|
1406
|
+
function chainsOf(patches) {
|
|
1407
|
+
var known = this.ids.length;
|
|
1408
|
+
var extends_ = patches.length >= known;
|
|
1409
|
+
for (var i = 0; extends_ && i < known; i++) {
|
|
1410
|
+
var patch = patches[i];
|
|
1411
|
+
extends_ = patch !== undefined && patch.patchId === this.ids[i] && patch.path === this.paths[i];
|
|
1412
|
+
}
|
|
1413
|
+
if (extends_ && patches.length === known) {
|
|
1414
|
+
return this.chains;
|
|
1415
|
+
}
|
|
1416
|
+
var chains = extends_ ? new Map(this.chains) : new Map();
|
|
1417
|
+
var from = extends_ ? known : 0;
|
|
1418
|
+
if (!extends_) {
|
|
1419
|
+
this.ids = [];
|
|
1420
|
+
this.paths = [];
|
|
1421
|
+
this.prefixes = new Map();
|
|
1422
|
+
}
|
|
1423
|
+
for (var _i = from; _i < patches.length; _i++) {
|
|
1424
|
+
var _chains$get;
|
|
1425
|
+
var _patch = patches[_i];
|
|
1426
|
+
if (_patch === undefined) continue;
|
|
1427
|
+
var chain = extendChain((_chains$get = chains.get(_patch.path)) !== null && _chains$get !== void 0 ? _chains$get : EMPTY_CHAIN, _patch.patchId);
|
|
1428
|
+
chains.set(_patch.path, chain);
|
|
1429
|
+
var prefixes = this.prefixes.get(_patch.path);
|
|
1430
|
+
if (prefixes === undefined) this.prefixes.set(_patch.path, [chain]);else prefixes.push(chain);
|
|
1431
|
+
this.ids.push(_patch.patchId);
|
|
1432
|
+
this.paths.push(_patch.path);
|
|
1433
|
+
}
|
|
1434
|
+
this.chains = chains;
|
|
1435
|
+
this.epochValue++;
|
|
1436
|
+
return chains;
|
|
1437
|
+
}
|
|
1438
|
+
|
|
1439
|
+
/**
|
|
1440
|
+
* A module's chain after its first `count` patches, of those the last
|
|
1441
|
+
* {@link chainsOf} was given: the version of content that stopped at a
|
|
1442
|
+
* patch that does not apply, so the version names the chain the content
|
|
1443
|
+
* is AT. Hashes nothing.
|
|
1444
|
+
*/
|
|
1445
|
+
}, {
|
|
1446
|
+
key: "chainAt",
|
|
1447
|
+
value: function chainAt(path, count) {
|
|
1448
|
+
var _this$prefixes$get, _ref, _prefixes;
|
|
1449
|
+
if (count <= 0) return EMPTY_CHAIN;
|
|
1450
|
+
var prefixes = (_this$prefixes$get = this.prefixes.get(path)) !== null && _this$prefixes$get !== void 0 ? _this$prefixes$get : [];
|
|
1451
|
+
return (_ref = (_prefixes = prefixes[Math.min(count, prefixes.length) - 1]) !== null && _prefixes !== void 0 ? _prefixes : this.chains.get(path)) !== null && _ref !== void 0 ? _ref : EMPTY_CHAIN;
|
|
1452
|
+
}
|
|
1453
|
+
|
|
1454
|
+
/** See {@link epochValue}. */
|
|
1455
|
+
}, {
|
|
1456
|
+
key: "epoch",
|
|
1457
|
+
value: function epoch() {
|
|
1458
|
+
return "".concat(this.instance, "-").concat(this.epochValue);
|
|
1459
|
+
}
|
|
1460
|
+
}]);
|
|
1461
|
+
}();
|
|
1462
|
+
|
|
1463
|
+
/** {@link ModuleVersion} on the wire. */
|
|
1464
|
+
var ModuleVersionSchema = z.object({
|
|
1465
|
+
schema: z.string(),
|
|
1466
|
+
base: z.string(),
|
|
1467
|
+
chain: z.string()
|
|
1468
|
+
});
|
|
1469
|
+
|
|
1470
|
+
/** {@link ProjectVersion} on the wire. */
|
|
1471
|
+
var ProjectVersionSchema = z.object({
|
|
1472
|
+
build: z.string(),
|
|
1473
|
+
seq: z.number()
|
|
1474
|
+
});
|
|
1475
|
+
|
|
1305
1476
|
/**
|
|
1306
1477
|
* The wire shapes for reading history.
|
|
1307
1478
|
*
|
|
@@ -1423,6 +1594,10 @@ var ModuleComparison = z.object({
|
|
|
1423
1594
|
verdict: RestoreVerdict
|
|
1424
1595
|
});
|
|
1425
1596
|
|
|
1597
|
+
/** An `EntryVersion` on the wire. */
|
|
1598
|
+
var EntryVersionSchema = ModuleVersionSchema.extend({
|
|
1599
|
+
key: z.string()
|
|
1600
|
+
});
|
|
1426
1601
|
var ModuleFilePath = z.string().refine(function (_path) {
|
|
1427
1602
|
return true;
|
|
1428
1603
|
} // TODO: validation
|
|
@@ -2045,11 +2220,112 @@ var Api = {
|
|
|
2045
2220
|
})])
|
|
2046
2221
|
}
|
|
2047
2222
|
},
|
|
2223
|
+
/**
|
|
2224
|
+
* What each module IS, right now: its version (`schema`, `base`, `chain`),
|
|
2225
|
+
* and the project version that is relative to. What `/stat` answers, per
|
|
2226
|
+
* module; see `docs/plans/one-content-model.md`, "The Studio loads
|
|
2227
|
+
* everything from one place".
|
|
2228
|
+
*
|
|
2229
|
+
* It has its own ETag, so a client that sends it back (`If-None-Match`)
|
|
2230
|
+
* gets a 304 and no body while nothing has moved. Computed from versions
|
|
2231
|
+
* kept as content changes: answering it hashes nothing.
|
|
2232
|
+
*
|
|
2233
|
+
* "Since `seq` N" (`?since=N`, with the ETag of the manifest the client
|
|
2234
|
+
* holds, which was answered at `seq` N, as `If-None-Match`): the modules
|
|
2235
|
+
* whose version moved since that manifest, and no others, with `since: N`
|
|
2236
|
+
* on the answer to say it is a delta. The ETag is in the body too (`tag`),
|
|
2237
|
+
* for a client that reads answers through `ValClient`, which hands over
|
|
2238
|
+
* the body and not the headers. Answered whole -- no `since` -- when the server no
|
|
2239
|
+
* longer holds the manifest that ETag names (another process, another
|
|
2240
|
+
* build, too long ago), and by a server of the release before, which
|
|
2241
|
+
* ignores the parameter. Optional both ways.
|
|
2242
|
+
*/
|
|
2243
|
+
"/manifest": {
|
|
2244
|
+
GET: {
|
|
2245
|
+
req: {
|
|
2246
|
+
cookies: {
|
|
2247
|
+
val_session: z.string().optional()
|
|
2248
|
+
},
|
|
2249
|
+
headers: {
|
|
2250
|
+
"if-none-match": z.string().optional()
|
|
2251
|
+
},
|
|
2252
|
+
query: {
|
|
2253
|
+
since: onlyOneStringQueryParam.optional()
|
|
2254
|
+
}
|
|
2255
|
+
},
|
|
2256
|
+
res: z.union([unauthorizedResponse, z.object({
|
|
2257
|
+
status: z.literal(500),
|
|
2258
|
+
json: GenericError
|
|
2259
|
+
}), z.object({
|
|
2260
|
+
status: z.literal(200),
|
|
2261
|
+
json: z.object({
|
|
2262
|
+
project: ProjectVersionSchema,
|
|
2263
|
+
modules: z.record(ModuleFilePath, ModuleVersionSchema),
|
|
2264
|
+
/** This answer's ETag, as the header has it. */
|
|
2265
|
+
tag: z.string().optional(),
|
|
2266
|
+
/**
|
|
2267
|
+
* Present on a delta only: the `seq` of the manifest it is
|
|
2268
|
+
* relative to, whose modules that are not named here did not
|
|
2269
|
+
* move. Absent: every module is named.
|
|
2270
|
+
*/
|
|
2271
|
+
since: z.number().optional(),
|
|
2272
|
+
/**
|
|
2273
|
+
* The build that answered is a previous build still answering
|
|
2274
|
+
* (http mode: the content service counted a newer one live).
|
|
2275
|
+
* Its versions are right for its own bases, which the site does
|
|
2276
|
+
* not serve any more: a Studio does not move to them, and asks
|
|
2277
|
+
* again. Optional: absent from a live build and a server of the
|
|
2278
|
+
* release before.
|
|
2279
|
+
*/
|
|
2280
|
+
superseded: z["boolean"]().optional(),
|
|
2281
|
+
/**
|
|
2282
|
+
* The answering build's `val.modules` registry, as an entry a
|
|
2283
|
+
* Studio can import (`valModulesEntryPath`), and the Val it
|
|
2284
|
+
* runs: where a Studio opened on another build takes this
|
|
2285
|
+
* build's code in without reloading the page. Only a build the
|
|
2286
|
+
* platform made and names (`publishBuild`) has one. Optional
|
|
2287
|
+
* both ways: absent from any other server (local development,
|
|
2288
|
+
* where HMR brings the code; a self-hosted deploy) and from a
|
|
2289
|
+
* server of the release before, and ignored by a Studio of the
|
|
2290
|
+
* release before.
|
|
2291
|
+
*/
|
|
2292
|
+
registry: z.object({
|
|
2293
|
+
entry: z.string(),
|
|
2294
|
+
core: z.string()
|
|
2295
|
+
}).optional()
|
|
2296
|
+
})
|
|
2297
|
+
}),
|
|
2298
|
+
/*
|
|
2299
|
+
* Nothing moved since the ETag the client sent. No body on the wire;
|
|
2300
|
+
* the client reads it as `json: null`.
|
|
2301
|
+
*/
|
|
2302
|
+
z.object({
|
|
2303
|
+
status: z.literal(304),
|
|
2304
|
+
json: z["null"]().optional()
|
|
2305
|
+
})])
|
|
2306
|
+
}
|
|
2307
|
+
},
|
|
2048
2308
|
"/stat": {
|
|
2049
2309
|
POST: {
|
|
2050
2310
|
req: {
|
|
2051
2311
|
body: z.object({
|
|
2312
|
+
/**
|
|
2313
|
+
* @deprecated Echoed by the Studio, and compared by the fs and
|
|
2314
|
+
* memory long poll to see that another build answers. This
|
|
2315
|
+
* release's Studio reads each module's version from
|
|
2316
|
+
* `GET /manifest`; goes once the long poll compares the echoed
|
|
2317
|
+
* version instead (see "Release-delayed deletions" in
|
|
2318
|
+
* `docs/plans/one-content-model.md`).
|
|
2319
|
+
*/
|
|
2052
2320
|
sourcesSha: z.string(),
|
|
2321
|
+
/**
|
|
2322
|
+
* @deprecated Echoed by the Studio, and compared by the fs and
|
|
2323
|
+
* memory long poll to see that another build answers. This
|
|
2324
|
+
* release's Studio reads each module's version from
|
|
2325
|
+
* `GET /manifest`; goes once the long poll compares the echoed
|
|
2326
|
+
* version instead (see "Release-delayed deletions" in
|
|
2327
|
+
* `docs/plans/one-content-model.md`).
|
|
2328
|
+
*/
|
|
2053
2329
|
schemaSha: z.string(),
|
|
2054
2330
|
baseSha: z.string(),
|
|
2055
2331
|
patches: z.array(z.string()).optional(),
|
|
@@ -2079,7 +2355,21 @@ var Api = {
|
|
|
2079
2355
|
json: z.union([z.object({
|
|
2080
2356
|
type: z.union([z.literal("request-again"), z.literal("no-change"), z.literal("did-change")]),
|
|
2081
2357
|
baseSha: z.string(),
|
|
2358
|
+
/**
|
|
2359
|
+
* @deprecated Read by a Studio of the release before
|
|
2360
|
+
* (`BaseAlignment`, `SchemaFreshnessWatch`); this release's
|
|
2361
|
+
* only echoes it, and reads each module's version from
|
|
2362
|
+
* `GET /manifest`. Goes in two steps (see "Release-delayed
|
|
2363
|
+
* deletions" in `docs/plans/one-content-model.md`).
|
|
2364
|
+
*/
|
|
2082
2365
|
schemaSha: z.string(),
|
|
2366
|
+
/**
|
|
2367
|
+
* @deprecated Read by a Studio of the release before
|
|
2368
|
+
* (`BaseAlignment`, `SchemaFreshnessWatch`); this release's
|
|
2369
|
+
* only echoes it, and reads each module's version from
|
|
2370
|
+
* `GET /manifest`. Goes in two steps (see "Release-delayed
|
|
2371
|
+
* deletions" in `docs/plans/one-content-model.md`).
|
|
2372
|
+
*/
|
|
2083
2373
|
sourcesSha: z.string(),
|
|
2084
2374
|
patches: z.array(PatchId),
|
|
2085
2375
|
/**
|
|
@@ -2177,13 +2467,34 @@ var Api = {
|
|
|
2177
2467
|
* the git mirror's or the one content reports for the project.
|
|
2178
2468
|
* Absent where there is no history to show.
|
|
2179
2469
|
*/
|
|
2180
|
-
historyBranch: z.string().optional()
|
|
2470
|
+
historyBranch: z.string().optional(),
|
|
2471
|
+
/**
|
|
2472
|
+
* What this answer is relative to: the build that served it
|
|
2473
|
+
* and how far along the project's changes it is (`seq`). Of two
|
|
2474
|
+
* answers, the higher `seq` is the newer. Optional: a server
|
|
2475
|
+
* that predates versions sends none.
|
|
2476
|
+
*/
|
|
2477
|
+
version: ProjectVersionSchema.optional()
|
|
2181
2478
|
}), z.object({
|
|
2182
2479
|
type: z.literal("use-websocket"),
|
|
2183
2480
|
url: z.string(),
|
|
2184
2481
|
nonce: z.string(),
|
|
2185
2482
|
baseSha: z.string(),
|
|
2483
|
+
/**
|
|
2484
|
+
* @deprecated Read by a Studio of the release before
|
|
2485
|
+
* (`BaseAlignment`, `SchemaFreshnessWatch`); this release's
|
|
2486
|
+
* only echoes it, and reads each module's version from
|
|
2487
|
+
* `GET /manifest`. Goes in two steps (see "Release-delayed
|
|
2488
|
+
* deletions" in `docs/plans/one-content-model.md`).
|
|
2489
|
+
*/
|
|
2186
2490
|
schemaSha: z.string(),
|
|
2491
|
+
/**
|
|
2492
|
+
* @deprecated Read by a Studio of the release before
|
|
2493
|
+
* (`BaseAlignment`, `SchemaFreshnessWatch`); this release's
|
|
2494
|
+
* only echoes it, and reads each module's version from
|
|
2495
|
+
* `GET /manifest`. Goes in two steps (see "Release-delayed
|
|
2496
|
+
* deletions" in `docs/plans/one-content-model.md`).
|
|
2497
|
+
*/
|
|
2187
2498
|
sourcesSha: z.string(),
|
|
2188
2499
|
/**
|
|
2189
2500
|
* The commit this deployment was built from.
|
|
@@ -2305,7 +2616,14 @@ var Api = {
|
|
|
2305
2616
|
* the git mirror's or the one content reports for the project.
|
|
2306
2617
|
* Absent where there is no history to show.
|
|
2307
2618
|
*/
|
|
2308
|
-
historyBranch: z.string().optional()
|
|
2619
|
+
historyBranch: z.string().optional(),
|
|
2620
|
+
/**
|
|
2621
|
+
* What this answer is relative to: the build that served it
|
|
2622
|
+
* and how far along the project's changes it is (`seq`). Of two
|
|
2623
|
+
* answers, the higher `seq` is the newer. Optional: a server
|
|
2624
|
+
* that predates versions sends none.
|
|
2625
|
+
*/
|
|
2626
|
+
version: ProjectVersionSchema.optional()
|
|
2309
2627
|
})])
|
|
2310
2628
|
})])
|
|
2311
2629
|
}
|
|
@@ -2347,6 +2665,14 @@ var Api = {
|
|
|
2347
2665
|
path: z.string().optional(),
|
|
2348
2666
|
cookies: {
|
|
2349
2667
|
val_session: z.string().optional()
|
|
2668
|
+
},
|
|
2669
|
+
/*
|
|
2670
|
+
* Carried to content, which answers 304 when it names the answer
|
|
2671
|
+
* (`/project-source` does: valbuild/home sends its `ETag`). The
|
|
2672
|
+
* `ETag` comes back as a header, and a 304 with no body.
|
|
2673
|
+
*/
|
|
2674
|
+
headers: {
|
|
2675
|
+
"if-none-match": z.string().optional()
|
|
2350
2676
|
}
|
|
2351
2677
|
},
|
|
2352
2678
|
/*
|
|
@@ -2358,10 +2684,18 @@ var Api = {
|
|
|
2358
2684
|
* `{ message }` rather than throwing, because a publish failing is
|
|
2359
2685
|
* something the editor has to be told about in words.
|
|
2360
2686
|
*/
|
|
2361
|
-
res: z.object({
|
|
2687
|
+
res: z.union([z.object({
|
|
2362
2688
|
status: z.number(),
|
|
2363
2689
|
json: z.unknown()
|
|
2364
|
-
})
|
|
2690
|
+
}),
|
|
2691
|
+
/*
|
|
2692
|
+
* Content said the caller holds the answer already (`If-None-Match`).
|
|
2693
|
+
* No body on the wire: a 304 cannot carry one.
|
|
2694
|
+
*/
|
|
2695
|
+
z.object({
|
|
2696
|
+
status: z.literal(304),
|
|
2697
|
+
json: z["null"]().optional()
|
|
2698
|
+
})])
|
|
2365
2699
|
},
|
|
2366
2700
|
POST: {
|
|
2367
2701
|
req: {
|
|
@@ -2745,6 +3079,26 @@ var Api = {
|
|
|
2745
3079
|
parentRef: ParentRef,
|
|
2746
3080
|
headVersion: z.number().optional()
|
|
2747
3081
|
})
|
|
3082
|
+
}),
|
|
3083
|
+
/*
|
|
3084
|
+
* The content service failed to take a patch: it answered with a
|
|
3085
|
+
* server error, or could not be reached. Not the 400 above either,
|
|
3086
|
+
* because the patches are fine -- the Studio keeps them and offers
|
|
3087
|
+
* to send them again. `message` is for the editor and `details` (the
|
|
3088
|
+
* status line and the service's own words) for whoever they ask.
|
|
3089
|
+
*
|
|
3090
|
+
* As with the 403, the patches BEFORE the failed one were saved.
|
|
3091
|
+
*/
|
|
3092
|
+
z.object({
|
|
3093
|
+
status: z.literal(502),
|
|
3094
|
+
json: z.object({
|
|
3095
|
+
type: z.literal("service-error"),
|
|
3096
|
+
message: z.string(),
|
|
3097
|
+
details: z.string().optional(),
|
|
3098
|
+
newPatchIds: z.array(PatchId),
|
|
3099
|
+
parentRef: ParentRef,
|
|
3100
|
+
headVersion: z.number().optional()
|
|
3101
|
+
})
|
|
2748
3102
|
}), z.object({
|
|
2749
3103
|
status: z.literal(200),
|
|
2750
3104
|
json: z.object({
|
|
@@ -2780,7 +3134,9 @@ var Api = {
|
|
|
2780
3134
|
status: z.literal(500),
|
|
2781
3135
|
json: z.object({
|
|
2782
3136
|
message: z.string(),
|
|
2783
|
-
error: GenericError
|
|
3137
|
+
error: GenericError,
|
|
3138
|
+
/** What the content service said: its status line and words. */
|
|
3139
|
+
details: z.string().optional()
|
|
2784
3140
|
})
|
|
2785
3141
|
}), z.object({
|
|
2786
3142
|
status: z.literal(200),
|
|
@@ -2797,7 +3153,9 @@ var Api = {
|
|
|
2797
3153
|
})),
|
|
2798
3154
|
baseSha: z.string(),
|
|
2799
3155
|
error: GenericError.optional(),
|
|
2800
|
-
errors: z.record(PatchId, GenericError).optional()
|
|
3156
|
+
errors: z.record(PatchId, GenericError).optional(),
|
|
3157
|
+
/** The project version these patches were read at. See `/stat`. */
|
|
3158
|
+
version: ProjectVersionSchema.optional()
|
|
2801
3159
|
})
|
|
2802
3160
|
})])
|
|
2803
3161
|
}
|
|
@@ -2830,6 +3188,15 @@ var Api = {
|
|
|
2830
3188
|
})])
|
|
2831
3189
|
}
|
|
2832
3190
|
},
|
|
3191
|
+
/**
|
|
3192
|
+
* @deprecated The whole tree. Nothing of this release asks it: the Studio
|
|
3193
|
+
* reads modules by version (`GET /manifest`, `GET /modules/~`), the draft
|
|
3194
|
+
* readers and MCP read the head in-process. Kept, with `exclude_patches`
|
|
3195
|
+
* (`BaseAlignment`'s fetch) and the ignored `validate_sources` and
|
|
3196
|
+
* `validate_binary_files`, for a Studio of the release before; goes in the
|
|
3197
|
+
* release after the one that ships this (see "Release-delayed deletions"
|
|
3198
|
+
* in `docs/plans/one-content-model.md`).
|
|
3199
|
+
*/
|
|
2833
3200
|
"/sources/~": {
|
|
2834
3201
|
PUT: {
|
|
2835
3202
|
req: {
|
|
@@ -2881,9 +3248,97 @@ var Api = {
|
|
|
2881
3248
|
skipped: z.array(PatchId).optional(),
|
|
2882
3249
|
errors: z.record(PatchId, GenericError).optional()
|
|
2883
3250
|
}).optional(),
|
|
2884
|
-
validationErrors: z.record(z.string(), z.array(ValidationError)).optional()
|
|
2885
|
-
|
|
3251
|
+
validationErrors: z.record(z.string(), z.array(ValidationError)).optional(),
|
|
3252
|
+
/**
|
|
3253
|
+
* The version of `source` as answered: with the chain the
|
|
3254
|
+
* server applied, or the empty chain when it applied none
|
|
3255
|
+
* (`apply_patches=false`). Optional: an older server sends
|
|
3256
|
+
* none.
|
|
3257
|
+
*/
|
|
3258
|
+
version: ModuleVersionSchema.optional()
|
|
3259
|
+
})),
|
|
3260
|
+
/** The project version this tree was read at. See `/stat`. */
|
|
3261
|
+
project: ProjectVersionSchema.optional()
|
|
3262
|
+
})
|
|
3263
|
+
})])
|
|
3264
|
+
}
|
|
3265
|
+
},
|
|
3266
|
+
/**
|
|
3267
|
+
* One module by version: phase 6 of `docs/plans/one-content-model.md`
|
|
3268
|
+
* ("The Studio loads everything from one place"). `GET /modules/~<path>`,
|
|
3269
|
+
* with `v` the version wanted as its `moduleKey` (the newest when absent;
|
|
3270
|
+
* a base is a version with the empty chain) and `have` the version the
|
|
3271
|
+
* caller holds: when the caller's chain is a start of the answer's under
|
|
3272
|
+
* the same schema and base, the answer is the patches from it (`delta`),
|
|
3273
|
+
* otherwise the whole module. An answer for an asked `v` is immutable.
|
|
3274
|
+
*/
|
|
3275
|
+
"/modules/~": {
|
|
3276
|
+
GET: {
|
|
3277
|
+
req: {
|
|
3278
|
+
path: z.string(),
|
|
3279
|
+
query: {
|
|
3280
|
+
v: onlyOneStringQueryParam.optional(),
|
|
3281
|
+
have: onlyOneStringQueryParam.optional()
|
|
3282
|
+
},
|
|
3283
|
+
cookies: {
|
|
3284
|
+
val_session: z.string().optional()
|
|
3285
|
+
}
|
|
3286
|
+
},
|
|
3287
|
+
res: z.union([unauthorizedResponse, z.object({
|
|
3288
|
+
status: z.literal(404),
|
|
3289
|
+
json: z.object({
|
|
3290
|
+
message: z.string(),
|
|
3291
|
+
/** The version held now, when the one asked for is not. */
|
|
3292
|
+
current: ModuleVersionSchema.optional()
|
|
2886
3293
|
})
|
|
3294
|
+
}), z.object({
|
|
3295
|
+
status: z.literal(500),
|
|
3296
|
+
json: GenericError
|
|
3297
|
+
}), z.object({
|
|
3298
|
+
status: z.literal(200),
|
|
3299
|
+
headers: z.object({
|
|
3300
|
+
"Cache-Control": z.string()
|
|
3301
|
+
}).optional(),
|
|
3302
|
+
json: z.union([z.object({
|
|
3303
|
+
kind: z.literal("module"),
|
|
3304
|
+
version: ModuleVersionSchema,
|
|
3305
|
+
source: z.any(),
|
|
3306
|
+
/**
|
|
3307
|
+
* The `.jsonValues()` entries the module's chain touched, read,
|
|
3308
|
+
* each with its own version. Every other entry is the base's,
|
|
3309
|
+
* behind its marker in `source`.
|
|
3310
|
+
*/
|
|
3311
|
+
entries: z.record(z.string(), z.union([z.object({
|
|
3312
|
+
content: z.any(),
|
|
3313
|
+
version: EntryVersionSchema
|
|
3314
|
+
}), z.object({
|
|
3315
|
+
deleted: z.literal(true),
|
|
3316
|
+
version: EntryVersionSchema
|
|
3317
|
+
}), z.object({
|
|
3318
|
+
error: z.string(),
|
|
3319
|
+
/** The patch that made it unreadable, when known. */
|
|
3320
|
+
patchId: PatchId.optional(),
|
|
3321
|
+
version: EntryVersionSchema
|
|
3322
|
+
})])),
|
|
3323
|
+
/**
|
|
3324
|
+
* The patch ids its chain is made of, in chain order: what a
|
|
3325
|
+
* client holding this knows the server already has. Optional:
|
|
3326
|
+
* a server of the release before sends none.
|
|
3327
|
+
*/
|
|
3328
|
+
patches: z.array(PatchId).optional(),
|
|
3329
|
+
failed: z.object({
|
|
3330
|
+
patchId: PatchId,
|
|
3331
|
+
message: z.string()
|
|
3332
|
+
}).optional()
|
|
3333
|
+
}), z.object({
|
|
3334
|
+
kind: z.literal("delta"),
|
|
3335
|
+
from: ModuleVersionSchema,
|
|
3336
|
+
to: ModuleVersionSchema,
|
|
3337
|
+
patches: z.array(z.object({
|
|
3338
|
+
patchId: PatchId,
|
|
3339
|
+
patch: Patch
|
|
3340
|
+
}))
|
|
3341
|
+
})])
|
|
2887
3342
|
})])
|
|
2888
3343
|
}
|
|
2889
3344
|
},
|
|
@@ -3607,7 +4062,14 @@ var Api = {
|
|
|
3607
4062
|
path: ModuleFilePath,
|
|
3608
4063
|
key: z.string(),
|
|
3609
4064
|
// The entry's JSON content (or null if the entry has no value).
|
|
3610
|
-
content: z.any()
|
|
4065
|
+
content: z.any(),
|
|
4066
|
+
/**
|
|
4067
|
+
* The version of the record module the entry was read from, with
|
|
4068
|
+
* the chain that was applied (the empty chain without
|
|
4069
|
+
* `apply_patches`). An entry's own version comes with entries
|
|
4070
|
+
* stored as modules of their own.
|
|
4071
|
+
*/
|
|
4072
|
+
version: ModuleVersionSchema.optional()
|
|
3611
4073
|
})
|
|
3612
4074
|
}),
|
|
3613
4075
|
// Shapes (2) and (3): `keys` or `offset`+`limit`.
|
|
@@ -3630,7 +4092,9 @@ var Api = {
|
|
|
3630
4092
|
// All-mode only: the resolved window and the record's total key count.
|
|
3631
4093
|
offset: z.number().optional(),
|
|
3632
4094
|
limit: z.number().optional(),
|
|
3633
|
-
total: z.number().optional()
|
|
4095
|
+
total: z.number().optional(),
|
|
4096
|
+
/** As for a single `key`. */
|
|
4097
|
+
version: ModuleVersionSchema.optional()
|
|
3634
4098
|
})
|
|
3635
4099
|
}), z.object({
|
|
3636
4100
|
status: z.literal(400),
|
|
@@ -4093,8 +4557,8 @@ var createValClient = function createValClient(host, config, options) {
|
|
|
4093
4557
|
body: anyReq.body !== undefined ? JSON.stringify(anyReq.body) : undefined
|
|
4094
4558
|
}).then(/*#__PURE__*/function () {
|
|
4095
4559
|
var _ref2 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee(res) {
|
|
4096
|
-
var _apiEndpoint$
|
|
4097
|
-
var contentTypeHeaders, json, valClientResult, message, responseResult;
|
|
4560
|
+
var _apiEndpoint$res2;
|
|
4561
|
+
var contentTypeHeaders, _apiEndpoint$res, notModified, json, valClientResult, message, responseResult;
|
|
4098
4562
|
return _regenerator().w(function (_context) {
|
|
4099
4563
|
while (1) switch (_context.n) {
|
|
4100
4564
|
case 0:
|
|
@@ -4112,10 +4576,38 @@ var createValClient = function createValClient(host, config, options) {
|
|
|
4112
4576
|
}
|
|
4113
4577
|
});
|
|
4114
4578
|
case 1:
|
|
4115
|
-
if (
|
|
4579
|
+
if (!(res.status === 304)) {
|
|
4580
|
+
_context.n = 3;
|
|
4581
|
+
break;
|
|
4582
|
+
}
|
|
4583
|
+
notModified = {
|
|
4584
|
+
status: 304,
|
|
4585
|
+
json: null
|
|
4586
|
+
};
|
|
4587
|
+
if (!(((_apiEndpoint$res = apiEndpoint.res) === null || _apiEndpoint$res === void 0 ? void 0 : _apiEndpoint$res.safeParse(notModified).success) === false)) {
|
|
4116
4588
|
_context.n = 2;
|
|
4117
4589
|
break;
|
|
4118
4590
|
}
|
|
4591
|
+
return _context.a(2, {
|
|
4592
|
+
status: null,
|
|
4593
|
+
json: {
|
|
4594
|
+
type: "client_side_validation_error",
|
|
4595
|
+
message: "Invalid response (304 from a route that does not answer one). This could be a result of mismatched Val versions.",
|
|
4596
|
+
details: {
|
|
4597
|
+
validationError: "Unexpected 304",
|
|
4598
|
+
data: {
|
|
4599
|
+
status: res.status
|
|
4600
|
+
}
|
|
4601
|
+
}
|
|
4602
|
+
}
|
|
4603
|
+
});
|
|
4604
|
+
case 2:
|
|
4605
|
+
return _context.a(2, notModified);
|
|
4606
|
+
case 3:
|
|
4607
|
+
if (contentTypeHeaders !== null && contentTypeHeaders !== void 0 && contentTypeHeaders.includes("application/json")) {
|
|
4608
|
+
_context.n = 4;
|
|
4609
|
+
break;
|
|
4610
|
+
}
|
|
4119
4611
|
return _context.a(2, {
|
|
4120
4612
|
status: null,
|
|
4121
4613
|
json: {
|
|
@@ -4130,17 +4622,17 @@ var createValClient = function createValClient(host, config, options) {
|
|
|
4130
4622
|
}
|
|
4131
4623
|
}
|
|
4132
4624
|
});
|
|
4133
|
-
case
|
|
4134
|
-
_context.n =
|
|
4625
|
+
case 4:
|
|
4626
|
+
_context.n = 5;
|
|
4135
4627
|
return res.json();
|
|
4136
|
-
case
|
|
4628
|
+
case 5:
|
|
4137
4629
|
json = _context.v;
|
|
4138
4630
|
valClientResult = {
|
|
4139
4631
|
status: res.status,
|
|
4140
4632
|
json: json
|
|
4141
4633
|
};
|
|
4142
4634
|
if (!(res.status === 500)) {
|
|
4143
|
-
_context.n =
|
|
4635
|
+
_context.n = 6;
|
|
4144
4636
|
break;
|
|
4145
4637
|
}
|
|
4146
4638
|
Internal.log.error("Server responded with an error", json);
|
|
@@ -4152,10 +4644,10 @@ var createValClient = function createValClient(host, config, options) {
|
|
|
4152
4644
|
message: message
|
|
4153
4645
|
}
|
|
4154
4646
|
});
|
|
4155
|
-
case
|
|
4156
|
-
responseResult = (_apiEndpoint$
|
|
4647
|
+
case 6:
|
|
4648
|
+
responseResult = (_apiEndpoint$res2 = apiEndpoint.res) === null || _apiEndpoint$res2 === void 0 ? void 0 : _apiEndpoint$res2.safeParse(valClientResult);
|
|
4157
4649
|
if (!(responseResult && !responseResult.success)) {
|
|
4158
|
-
_context.n =
|
|
4650
|
+
_context.n = 7;
|
|
4159
4651
|
break;
|
|
4160
4652
|
}
|
|
4161
4653
|
return _context.a(2, {
|
|
@@ -4169,7 +4661,7 @@ var createValClient = function createValClient(host, config, options) {
|
|
|
4169
4661
|
}
|
|
4170
4662
|
}
|
|
4171
4663
|
});
|
|
4172
|
-
case
|
|
4664
|
+
case 7:
|
|
4173
4665
|
return _context.a(2, {
|
|
4174
4666
|
status: res.status,
|
|
4175
4667
|
json: json
|
|
@@ -4313,6 +4805,44 @@ var urlOf = function urlOf() {
|
|
|
4313
4805
|
return "".concat(route);
|
|
4314
4806
|
};
|
|
4315
4807
|
|
|
4808
|
+
/**
|
|
4809
|
+
* Which Val package, at which version, sent a request to Val Build.
|
|
4810
|
+
*
|
|
4811
|
+
* Sent on every request a Val package makes to Val's own services from a
|
|
4812
|
+
* server or a CLI -- the content host, `/api/login`, the analytics ingest --
|
|
4813
|
+
* so Val Build can tell which versions are still out there and which old
|
|
4814
|
+
* endpoints they still call. Without it an endpoint can never be retired with
|
|
4815
|
+
* any confidence: a request from a site nobody has upgraded in a year looks
|
|
4816
|
+
* exactly like one from yesterday's release.
|
|
4817
|
+
*
|
|
4818
|
+
* The value is a product token, `@valbuild/server@0.152.0`: the package whose
|
|
4819
|
+
* code built the request, because that is what decides which endpoints are
|
|
4820
|
+
* called. Not the framework binding or the app: those do not choose URLs.
|
|
4821
|
+
*
|
|
4822
|
+
* NOT sent from the browser. A custom header on a cross-origin request makes
|
|
4823
|
+
* the browser send a CORS preflight, and a host that does not list the header
|
|
4824
|
+
* in `Access-Control-Allow-Headers` refuses the request outright -- so adding
|
|
4825
|
+
* it there is a change to every such host first.
|
|
4826
|
+
*
|
|
4827
|
+
* It identifies the code, not the caller: anyone can send any value, so it is
|
|
4828
|
+
* for counting, never for deciding what a request may do.
|
|
4829
|
+
*/
|
|
4830
|
+
var VAL_CLIENT_HEADER = "x-val-client";
|
|
4831
|
+
|
|
4832
|
+
/** Packages that send {@link VAL_CLIENT_HEADER}. */
|
|
4833
|
+
|
|
4834
|
+
/**
|
|
4835
|
+
* `{ "x-val-client": "@valbuild/server@0.152.0" }`, to spread into a
|
|
4836
|
+
* request's headers.
|
|
4837
|
+
*
|
|
4838
|
+
* A version that could not be read is sent as `unknown` rather than leaving
|
|
4839
|
+
* the header out: "a server too broken to know its version" is still a thing
|
|
4840
|
+
* worth counting, and it is not the same as "a version older than the header".
|
|
4841
|
+
*/
|
|
4842
|
+
function valClientHeaders(pkg, version) {
|
|
4843
|
+
return _defineProperty({}, VAL_CLIENT_HEADER, "".concat(pkg, "@").concat(version || "unknown"));
|
|
4844
|
+
}
|
|
4845
|
+
|
|
4316
4846
|
/**
|
|
4317
4847
|
* Can this value, taken from one schema, be written into another?
|
|
4318
4848
|
*
|
|
@@ -4746,10 +5276,1504 @@ function chainHeadOf(reportedHead, listed) {
|
|
|
4746
5276
|
return (_listed = listed[listed.length - 1]) !== null && _listed !== void 0 ? _listed : null;
|
|
4747
5277
|
}
|
|
4748
5278
|
|
|
4749
|
-
|
|
4750
|
-
|
|
4751
|
-
|
|
4752
|
-
|
|
5279
|
+
/**
|
|
5280
|
+
* Where a build's `val.modules` registry can be imported from: phase 9 of
|
|
5281
|
+
* `docs/plans/one-content-model.md`, "Reloading the schema while the Studio
|
|
5282
|
+
* runs".
|
|
5283
|
+
*
|
|
5284
|
+
* A build made by `@valbuild/tanstack-build` (every build the platform
|
|
5285
|
+
* serves) emits its `val.modules` as an ES module of its own beside its
|
|
5286
|
+
* client chunks, named after the build: `/_app/val-modules-<build>.js`,
|
|
5287
|
+
* whose default export is the registry. The server running that build knows
|
|
5288
|
+
* its name (`VAL_BUILD`, Val's `publishBuild`) and names the entry on
|
|
5289
|
+
* `GET /manifest` (`registry`), so a Studio opened on an older build imports
|
|
5290
|
+
* the new one's code and reloads it in place (`HostStore.reloadCode`)
|
|
5291
|
+
* instead of being told to reload the page.
|
|
5292
|
+
*
|
|
5293
|
+
* The convention is the builder's and this file's together: the builder
|
|
5294
|
+
* writes the file (`valModulesEntryFile` in `@valbuild/tanstack-build`, which
|
|
5295
|
+
* does not depend on this package), and each side pins the same literal,
|
|
5296
|
+
* `val-modules-<build>.js` (`buildRegistry.test.ts` here, `valModules.test.ts`
|
|
5297
|
+
* there).
|
|
5298
|
+
*/
|
|
5299
|
+
|
|
5300
|
+
/** The file a build's registry is emitted as, among its client chunks. */
|
|
5301
|
+
function valModulesEntryFile(build) {
|
|
5302
|
+
return "val-modules-".concat(build, ".js");
|
|
5303
|
+
}
|
|
5304
|
+
|
|
5305
|
+
/** Where a build served by the platform serves its registry. */
|
|
5306
|
+
function valModulesEntryPath(build) {
|
|
5307
|
+
return "/_app/".concat(valModulesEntryFile(build));
|
|
5308
|
+
}
|
|
5309
|
+
|
|
5310
|
+
/**
|
|
5311
|
+
* What `GET /manifest` says about the code of the build that answered:
|
|
5312
|
+
* where its registry is, and which Val it runs. A Studio running another
|
|
5313
|
+
* Val cannot take the build's code in (its own stores would run against a
|
|
5314
|
+
* new core), so it reloads itself instead (`other-val-version`).
|
|
5315
|
+
*/
|
|
5316
|
+
|
|
5317
|
+
/*
|
|
5318
|
+
* How a patch op relates to a `.jsonValues()` record: which ops reach into an
|
|
5319
|
+
* entry, what a write of the whole record says about each entry, and how an
|
|
5320
|
+
* op inside an entry is rebased onto the entry's own file.
|
|
5321
|
+
*
|
|
5322
|
+
* Here, in shared, because the engine (`applyChain.ts`) is built on them and
|
|
5323
|
+
* runs wherever content is patched; the commit flow (`ValOps.prepare`), the
|
|
5324
|
+
* CLI's fixes and the language server use the same functions, through
|
|
5325
|
+
* `@valbuild/server`, which re-exports them. One rule, one implementation:
|
|
5326
|
+
* a draft that shows one thing while publishing writes another is the failure
|
|
5327
|
+
* nobody sees until it is published.
|
|
5328
|
+
*/
|
|
5329
|
+
|
|
5330
|
+
/**
|
|
5331
|
+
* Classification of a single patch op against a module's serialized schema,
|
|
5332
|
+
* used by the commit flow to route ops for `.jsonValues()` records:
|
|
5333
|
+
*
|
|
5334
|
+
* - `normal`: the op does not descend into a `.jsonValues()` entry; apply it to
|
|
5335
|
+
* the `.val.ts` as usual.
|
|
5336
|
+
* - `entry`: the op targets a `.jsonValues()` entry. `recordPath` is the path to
|
|
5337
|
+
* the record within the module source (empty for a root record/router),
|
|
5338
|
+
* `entryKey` is the entry key, and `subPath` is the remaining path inside the
|
|
5339
|
+
* entry (empty when the op targets the entry value itself, e.g. add/remove of
|
|
5340
|
+
* the whole entry).
|
|
5341
|
+
*/
|
|
5342
|
+
|
|
5343
|
+
/**
|
|
5344
|
+
* Walks the serialized schema following the op path. When a `.jsonValues()`
|
|
5345
|
+
* record is encountered, the next path segment is the entry key and everything
|
|
5346
|
+
* after it lives inside the entry's `*.val.json` (so it does not touch the
|
|
5347
|
+
* `.val.ts`). Returns `{ kind: "normal" }` when the op never enters a
|
|
5348
|
+
* `.jsonValues()` record.
|
|
5349
|
+
*/
|
|
5350
|
+
function classifyJsonValuesOp(schema, opPath) {
|
|
5351
|
+
var current = schema;
|
|
5352
|
+
var recordPath = [];
|
|
5353
|
+
for (var i = 0; i < opPath.length; i++) {
|
|
5354
|
+
if (!current) {
|
|
5355
|
+
return {
|
|
5356
|
+
kind: "normal"
|
|
5357
|
+
};
|
|
5358
|
+
}
|
|
5359
|
+
if (current.type === "record" && current.jsonValues) {
|
|
5360
|
+
return {
|
|
5361
|
+
kind: "entry",
|
|
5362
|
+
recordPath: recordPath.slice(),
|
|
5363
|
+
entryKey: opPath[i],
|
|
5364
|
+
subPath: opPath.slice(i + 1)
|
|
5365
|
+
};
|
|
5366
|
+
}
|
|
5367
|
+
var seg = opPath[i];
|
|
5368
|
+
current = descend(current, seg);
|
|
5369
|
+
recordPath.push(seg);
|
|
5370
|
+
}
|
|
5371
|
+
return {
|
|
5372
|
+
kind: "normal"
|
|
5373
|
+
};
|
|
5374
|
+
}
|
|
5375
|
+
function descend(schema, key) {
|
|
5376
|
+
switch (schema.type) {
|
|
5377
|
+
case "object":
|
|
5378
|
+
return schema.items[key];
|
|
5379
|
+
case "record":
|
|
5380
|
+
return schema.item;
|
|
5381
|
+
case "array":
|
|
5382
|
+
return schema.item;
|
|
5383
|
+
default:
|
|
5384
|
+
// Unions / primitives / leaf schemas: we cannot (or need not) descend
|
|
5385
|
+
// further to find a jsonValues record. Anything below is a normal
|
|
5386
|
+
// `.val.ts` edit.
|
|
5387
|
+
return undefined;
|
|
5388
|
+
}
|
|
5389
|
+
}
|
|
5390
|
+
|
|
5391
|
+
/**
|
|
5392
|
+
* Is this op a write of the WHOLE record at the root of a `.jsonValues()`
|
|
5393
|
+
* module?
|
|
5394
|
+
*
|
|
5395
|
+
* The one op {@link classifyJsonValuesOp} cannot classify: it finds the entry
|
|
5396
|
+
* key by walking the op path, and the root path has no segments to walk, so a
|
|
5397
|
+
* root write reads as `normal` and gets applied to the `.val.ts` - writing
|
|
5398
|
+
* markers over the `c.json(() => import(...))` calls that make the entries load
|
|
5399
|
+
* at all. {@link expandJsonValuesRootOp} turns it into ops that DO name a key,
|
|
5400
|
+
* which everything downstream already handles.
|
|
5401
|
+
*
|
|
5402
|
+
* `add` counts as well as `replace`: at the root both mean "the document is now
|
|
5403
|
+
* this" (see `JSONOps`), so both have to be expanded or the unexpanded one is
|
|
5404
|
+
* the same bug again.
|
|
5405
|
+
*
|
|
5406
|
+
* The value has to BE a record, and that is part of the question rather than a
|
|
5407
|
+
* check inside the expansion: a `.jsonValues()` record can be nullable, and
|
|
5408
|
+
* `null` at the root means the module no longer has a record at all - there are
|
|
5409
|
+
* no entries to write, and it is an ordinary `.val.ts` write, exactly as it was
|
|
5410
|
+
* before any of this existed. Same for any other non-record value: not a
|
|
5411
|
+
* whole-record write, so not this conversion's to route.
|
|
5412
|
+
*/
|
|
5413
|
+
function isJsonValuesRootOp(schema, op) {
|
|
5414
|
+
return (op.op === "replace" || op.op === "add") && op.path.length === 0 && isJsonValuesRecord(op.value) && schema.type === "record" && schema.jsonValues === true;
|
|
5415
|
+
}
|
|
5416
|
+
function isJsonValuesRecord(value) {
|
|
5417
|
+
return _typeof(value) === "object" && value !== null && !Array.isArray(value);
|
|
5418
|
+
}
|
|
5419
|
+
|
|
5420
|
+
/**
|
|
5421
|
+
* One entry as it stands right now, for {@link expandJsonValuesRootOp} to
|
|
5422
|
+
* expand against.
|
|
5423
|
+
*
|
|
5424
|
+
* The key set decides add-vs-replace-vs-remove. The content, where the caller
|
|
5425
|
+
* has it, decides whether an op is emitted at all: an entry whose content is
|
|
5426
|
+
* already what the root write says is left alone rather than rewritten, so
|
|
5427
|
+
* putting a module back does not touch every `*.val.json` it did not change.
|
|
5428
|
+
* `undefined` means "this entry exists, content unknown" - which is what the
|
|
5429
|
+
* Studio's draft source has, because the source holds markers - and yields a
|
|
5430
|
+
* `replace`, the safe answer.
|
|
5431
|
+
*/
|
|
5432
|
+
|
|
5433
|
+
/**
|
|
5434
|
+
* Fans a whole-record write at a `.jsonValues()` module's root out into ops
|
|
5435
|
+
* that each name an entry key.
|
|
5436
|
+
*
|
|
5437
|
+
* This is THE conversion, and it has exactly one implementation on purpose: the
|
|
5438
|
+
* commit flow (`ValOps.prepare`) and the engine that builds draft content
|
|
5439
|
+
* (`applyPatchRecord`) both expand through here, so a draft
|
|
5440
|
+
* cannot show something other than what publishing writes. A second
|
|
5441
|
+
* implementation of the same rule would differ silently, which is the whole
|
|
5442
|
+
* failure mode this exists to prevent.
|
|
5443
|
+
*
|
|
5444
|
+
* The value must be the entries' CONTENT. A module's Source is markers, not
|
|
5445
|
+
* content, so a Source handed over here is refused rather than written: that is
|
|
5446
|
+
* the shape of the original bug (a revert replaying the archived Source), and
|
|
5447
|
+
* it is not recoverable afterwards - the markers replace the entries' content
|
|
5448
|
+
* on disk.
|
|
5449
|
+
*/
|
|
5450
|
+
function expandJsonValuesRootOp(op, currentEntries) {
|
|
5451
|
+
var valueRes = jsonValuesRootValue(op);
|
|
5452
|
+
if (result.isErr(valueRes)) {
|
|
5453
|
+
return valueRes;
|
|
5454
|
+
}
|
|
5455
|
+
var value = valueRes.value;
|
|
5456
|
+
var expanded = [];
|
|
5457
|
+
for (var _i = 0, _Object$keys = Object.keys(value); _i < _Object$keys.length; _i++) {
|
|
5458
|
+
var entryKey = _Object$keys[_i];
|
|
5459
|
+
var write = writeForEntry(value, entryKey, currentEntries);
|
|
5460
|
+
if (write !== null) {
|
|
5461
|
+
expanded.push(write);
|
|
5462
|
+
}
|
|
5463
|
+
}
|
|
5464
|
+
var _iterator = _createForOfIteratorHelper(currentEntries.keys()),
|
|
5465
|
+
_step;
|
|
5466
|
+
try {
|
|
5467
|
+
for (_iterator.s(); !(_step = _iterator.n()).done;) {
|
|
5468
|
+
var _entryKey = _step.value;
|
|
5469
|
+
if (!Object.prototype.hasOwnProperty.call(value, _entryKey)) {
|
|
5470
|
+
expanded.push({
|
|
5471
|
+
op: "remove",
|
|
5472
|
+
path: [_entryKey]
|
|
5473
|
+
});
|
|
5474
|
+
}
|
|
5475
|
+
}
|
|
5476
|
+
} catch (err) {
|
|
5477
|
+
_iterator.e(err);
|
|
5478
|
+
} finally {
|
|
5479
|
+
_iterator.f();
|
|
5480
|
+
}
|
|
5481
|
+
return result.ok(expanded);
|
|
5482
|
+
}
|
|
5483
|
+
|
|
5484
|
+
/**
|
|
5485
|
+
* What a whole-record write says about ONE entry: the same rule
|
|
5486
|
+
* {@link expandJsonValuesRootOp} applies to all of them.
|
|
5487
|
+
*
|
|
5488
|
+
* For a reader that is about one entry. Expanding the whole record to find the
|
|
5489
|
+
* one op that names its key costs an op per entry per entry read - the record
|
|
5490
|
+
* squared, on exactly the large records this feature is for. The rule itself is
|
|
5491
|
+
* `writeForEntry`, shared with the expansion above, so the two cannot come to
|
|
5492
|
+
* disagree; only the loop around it differs.
|
|
5493
|
+
*
|
|
5494
|
+
* `null` means the write says nothing about this entry: it holds that content
|
|
5495
|
+
* already, or the record does not name it and it does not exist.
|
|
5496
|
+
*/
|
|
5497
|
+
function expandJsonValuesRootOpForKey(op, entryKey, currentEntries) {
|
|
5498
|
+
var valueRes = jsonValuesRootValue(op);
|
|
5499
|
+
if (result.isErr(valueRes)) {
|
|
5500
|
+
return valueRes;
|
|
5501
|
+
}
|
|
5502
|
+
var value = valueRes.value;
|
|
5503
|
+
if (Object.prototype.hasOwnProperty.call(value, entryKey)) {
|
|
5504
|
+
return result.ok(writeForEntry(value, entryKey, currentEntries));
|
|
5505
|
+
}
|
|
5506
|
+
if (currentEntries.has(entryKey)) {
|
|
5507
|
+
return result.ok({
|
|
5508
|
+
op: "remove",
|
|
5509
|
+
path: [entryKey]
|
|
5510
|
+
});
|
|
5511
|
+
}
|
|
5512
|
+
return result.ok(null);
|
|
5513
|
+
}
|
|
5514
|
+
|
|
5515
|
+
/**
|
|
5516
|
+
* The record a whole-record write carries, once it is known to be one.
|
|
5517
|
+
*
|
|
5518
|
+
* Every entry's value is checked here rather than where each one is written:
|
|
5519
|
+
* a marker anywhere in the record means the writer handed over a module's
|
|
5520
|
+
* Source, and the answer to that is to refuse the write, not to write the rest
|
|
5521
|
+
* of it. Both entry points ask, so an entry read reports it too rather than
|
|
5522
|
+
* showing a draft of a patch that publishing will refuse.
|
|
5523
|
+
*/
|
|
5524
|
+
function jsonValuesRootValue(op) {
|
|
5525
|
+
if (op.op !== "replace" && op.op !== "add") {
|
|
5526
|
+
return result.err(new PatchError("Cannot '".concat(op.op, "' the root of a .jsonValues() record: only add and replace write the whole record")));
|
|
5527
|
+
}
|
|
5528
|
+
var value = op.value;
|
|
5529
|
+
if (!isJsonValuesRecord(value)) {
|
|
5530
|
+
return result.err(new PatchError("Cannot write the root of a .jsonValues() record with a non-record value: it must be an object of entry key to entry content"));
|
|
5531
|
+
}
|
|
5532
|
+
for (var _i2 = 0, _Object$keys2 = Object.keys(value); _i2 < _Object$keys2.length; _i2++) {
|
|
5533
|
+
var entryKey = _Object$keys2[_i2];
|
|
5534
|
+
/*
|
|
5535
|
+
* A marker is what a module's Source holds WHERE THE CONTENT IS NOT: it
|
|
5536
|
+
* says the content is in the entry's own file. Handed back as an entry's
|
|
5537
|
+
* value - which is what replaying an archived Source does - it would
|
|
5538
|
+
* replace that content with the note saying where it used to be, and
|
|
5539
|
+
* nothing downstream could tell the difference.
|
|
5540
|
+
*/
|
|
5541
|
+
if (Internal.isJson(value[entryKey])) {
|
|
5542
|
+
return result.err(new PatchError("Cannot write the .jsonValues() entry '".concat(entryKey, "' from a '").concat(VAL_EXTENSION, ": \"json\"' marker: a marker says the content is in the entry's own file, so write the entry's CONTENT here, not the module's Source")));
|
|
5543
|
+
}
|
|
5544
|
+
}
|
|
5545
|
+
return result.ok(value);
|
|
5546
|
+
}
|
|
5547
|
+
|
|
5548
|
+
/** THE rule, for one entry the record names. `null` = nothing to write. */
|
|
5549
|
+
function writeForEntry(value, entryKey, currentEntries) {
|
|
5550
|
+
var content = value[entryKey];
|
|
5551
|
+
if (!currentEntries.has(entryKey)) {
|
|
5552
|
+
return {
|
|
5553
|
+
op: "add",
|
|
5554
|
+
path: [entryKey],
|
|
5555
|
+
value: content
|
|
5556
|
+
};
|
|
5557
|
+
}
|
|
5558
|
+
var current = currentEntries.get(entryKey);
|
|
5559
|
+
if (current !== undefined && deepEqual(current, content)) {
|
|
5560
|
+
// Unchanged: emitting a replace here would rewrite the entry's file with
|
|
5561
|
+
// the bytes it already holds, so putting a module back would show up as a
|
|
5562
|
+
// change to every entry in it.
|
|
5563
|
+
return null;
|
|
5564
|
+
}
|
|
5565
|
+
return {
|
|
5566
|
+
op: "replace",
|
|
5567
|
+
path: [entryKey],
|
|
5568
|
+
value: content
|
|
5569
|
+
};
|
|
5570
|
+
}
|
|
5571
|
+
|
|
5572
|
+
/**
|
|
5573
|
+
* Rebases a patch op that targets a `.jsonValues()` entry's content so its paths
|
|
5574
|
+
* are relative to the entry's `*.val.json` root (drops the record + entry-key
|
|
5575
|
+
* prefix). Used to replay the op against the backing JSON file.
|
|
5576
|
+
*/
|
|
5577
|
+
function rebaseContentOp(op, prefixLen) {
|
|
5578
|
+
var path = op.path.slice(prefixLen);
|
|
5579
|
+
switch (op.op) {
|
|
5580
|
+
case "add":
|
|
5581
|
+
case "replace":
|
|
5582
|
+
case "test":
|
|
5583
|
+
return result.ok(_objectSpread2(_objectSpread2({}, op), {}, {
|
|
5584
|
+
path: path
|
|
5585
|
+
}));
|
|
5586
|
+
case "remove":
|
|
5587
|
+
{
|
|
5588
|
+
if (!array.isNonEmpty(path)) {
|
|
5589
|
+
return result.err(new PatchError("Cannot remove the root of a jsonValues entry"));
|
|
5590
|
+
}
|
|
5591
|
+
return result.ok(_objectSpread2(_objectSpread2({}, op), {}, {
|
|
5592
|
+
path: path
|
|
5593
|
+
}));
|
|
5594
|
+
}
|
|
5595
|
+
case "move":
|
|
5596
|
+
{
|
|
5597
|
+
var from = op.from.slice(prefixLen);
|
|
5598
|
+
if (!array.isNonEmpty(from)) {
|
|
5599
|
+
return result.err(new PatchError("Cannot move from the root of a jsonValues entry"));
|
|
5600
|
+
}
|
|
5601
|
+
return result.ok(_objectSpread2(_objectSpread2({}, op), {}, {
|
|
5602
|
+
path: path,
|
|
5603
|
+
from: from
|
|
5604
|
+
}));
|
|
5605
|
+
}
|
|
5606
|
+
case "copy":
|
|
5607
|
+
return result.ok(_objectSpread2(_objectSpread2({}, op), {}, {
|
|
5608
|
+
path: path,
|
|
5609
|
+
from: op.from.slice(prefixLen)
|
|
5610
|
+
}));
|
|
5611
|
+
case "file":
|
|
5612
|
+
// A file op carries bytes, not content. The binary goes through the
|
|
5613
|
+
// normal upload pipeline; what lands in the entry is the `patch_id` that
|
|
5614
|
+
// marks the bytes as not yet committed, which the caller writes.
|
|
5615
|
+
return result.err(new PatchError("A file op is not a content op: rebase it via its path"));
|
|
5616
|
+
}
|
|
5617
|
+
}
|
|
5618
|
+
|
|
5619
|
+
/**
|
|
5620
|
+
* The entry a path names when it names a WHOLE entry of the root
|
|
5621
|
+
* `.jsonValues()` record, and `undefined` for anything else.
|
|
5622
|
+
*/
|
|
5623
|
+
function wholeEntryKey(serializedSchema, path) {
|
|
5624
|
+
if (!serializedSchema) {
|
|
5625
|
+
return undefined;
|
|
5626
|
+
}
|
|
5627
|
+
var cls = classifyJsonValuesOp(serializedSchema, path);
|
|
5628
|
+
return cls.kind === "entry" && cls.recordPath.length === 0 && cls.subPath.length === 0 ? cls.entryKey : undefined;
|
|
5629
|
+
}
|
|
5630
|
+
|
|
5631
|
+
/*
|
|
5632
|
+
* THE engine: one function that turns a module's content and a patch into
|
|
5633
|
+
* the module's next content. See `docs/plans/one-content-model.md`, "One
|
|
5634
|
+
* engine".
|
|
5635
|
+
*
|
|
5636
|
+
* It owns the rules the server's two readers used to follow each in their own
|
|
5637
|
+
* way (`ValOps.getSources` for the module, and a replay of its own for a
|
|
5638
|
+
* `.jsonValues()` entry, kept test-only in `legacyEngine.ts` in
|
|
5639
|
+
* `@valbuild/server` for the conformance cases):
|
|
5640
|
+
*
|
|
5641
|
+
* - where a file's `patch_id` lands: the field's path plus `nestedFilePath`,
|
|
5642
|
+
* after every other op of the patch;
|
|
5643
|
+
* - what an edit inside an entry does: applied to the entry's content when it
|
|
5644
|
+
* is loaded, held (`waiting`) until it is when it is not;
|
|
5645
|
+
* - what a whole entry added does: a marker in the module, content in the
|
|
5646
|
+
* entry;
|
|
5647
|
+
* - that a module's chain stops at its first failing patch, and that the
|
|
5648
|
+
* entries stop with it.
|
|
5649
|
+
*
|
|
5650
|
+
* Copy-on-write along each op's path: nothing is mutated, so a subtree no op
|
|
5651
|
+
* touched keeps its identity (which makes "did this path change?" a `===`),
|
|
5652
|
+
* and a patch costs the depth of its path, not the module's size. Every
|
|
5653
|
+
* container it copies is counted ({@link engineWork}).
|
|
5654
|
+
*/
|
|
5655
|
+
|
|
5656
|
+
/** What one `.jsonValues()` entry is at a point in a module's chain. */
|
|
5657
|
+
|
|
5658
|
+
/**
|
|
5659
|
+
* The ops held for an entry, newest first: a list that is extended without
|
|
5660
|
+
* copying, so a chain of N edits to an entry nobody loaded costs N, not N².
|
|
5661
|
+
*/
|
|
5662
|
+
|
|
5663
|
+
/**
|
|
5664
|
+
* A module's content: its source, and what its `.jsonValues()` entries are.
|
|
5665
|
+
*
|
|
5666
|
+
* `entries` holds only the entries the chain has touched. An entry it has
|
|
5667
|
+
* not is the base's: `waiting` on its own key with nothing held, as long as
|
|
5668
|
+
* the source has its marker ({@link entryState}). So a module with no
|
|
5669
|
+
* entries, or a chain that never touches one, costs nothing here.
|
|
5670
|
+
*/
|
|
5671
|
+
|
|
5672
|
+
/** One patch of a module's chain. */
|
|
5673
|
+
|
|
5674
|
+
var NO_ENTRIES = new Map();
|
|
5675
|
+
|
|
5676
|
+
/** A module's content with nothing applied yet: its base. */
|
|
5677
|
+
function baseModuleContent(source) {
|
|
5678
|
+
return {
|
|
5679
|
+
source: source,
|
|
5680
|
+
entries: NO_ENTRIES
|
|
5681
|
+
};
|
|
5682
|
+
}
|
|
5683
|
+
|
|
5684
|
+
// #region counted work
|
|
5685
|
+
|
|
5686
|
+
/**
|
|
5687
|
+
* What a copied container costs, per slot copied (an object's entry or an
|
|
5688
|
+
* array's element): one reference. Strings and numbers are shared, never
|
|
5689
|
+
* copied, so this is what a copy really moves -- and what the whole module
|
|
5690
|
+
* cost per patch before copy-on-write (`applyPatch(deepClone(module))`).
|
|
5691
|
+
*/
|
|
5692
|
+
var SLOT_BYTES = 8;
|
|
5693
|
+
var applies = 0;
|
|
5694
|
+
var clonedBytes = 0;
|
|
5695
|
+
|
|
5696
|
+
/**
|
|
5697
|
+
* The work the engine has done in this process: patches applied
|
|
5698
|
+
* (`engine:apply-patch`) and bytes copied (`engine:clone-bytes`, see
|
|
5699
|
+
* {@link SLOT_BYTES}). A test takes it before and after; never reset, so two
|
|
5700
|
+
* readers of it cannot disturb each other.
|
|
5701
|
+
*/
|
|
5702
|
+
function engineWork() {
|
|
5703
|
+
return {
|
|
5704
|
+
applyPatch: applies,
|
|
5705
|
+
cloneBytes: clonedBytes
|
|
5706
|
+
};
|
|
5707
|
+
}
|
|
5708
|
+
|
|
5709
|
+
// #region the patch
|
|
5710
|
+
|
|
5711
|
+
/**
|
|
5712
|
+
* Applies one patch to a module's content. Nothing is mutated: the module
|
|
5713
|
+
* passed in is still what it was, and the one returned shares every subtree
|
|
5714
|
+
* the patch did not touch.
|
|
5715
|
+
*
|
|
5716
|
+
* `failed` means the patch does not apply, and then none of it does: half a
|
|
5717
|
+
* patch is half an edit. The caller stops the module's chain there.
|
|
5718
|
+
*
|
|
5719
|
+
* `schema` is the module's serialized schema: it says which ops reach into a
|
|
5720
|
+
* `.jsonValues()` entry. Without one, every op is an op on the source.
|
|
5721
|
+
*/
|
|
5722
|
+
function applyPatchRecord(module, record, schema) {
|
|
5723
|
+
applies++;
|
|
5724
|
+
var patchId = record.patchId;
|
|
5725
|
+
var fail = function fail(message) {
|
|
5726
|
+
return {
|
|
5727
|
+
status: "failed",
|
|
5728
|
+
patchId: patchId,
|
|
5729
|
+
message: message
|
|
5730
|
+
};
|
|
5731
|
+
};
|
|
5732
|
+
var source = module.source;
|
|
5733
|
+
var entries = null;
|
|
5734
|
+
var stateOf = function stateOf(key) {
|
|
5735
|
+
var _get;
|
|
5736
|
+
return (_get = (entries !== null && entries !== void 0 ? entries : module.entries).get(key)) !== null && _get !== void 0 ? _get : implicitState(source, key);
|
|
5737
|
+
};
|
|
5738
|
+
var setEntry = function setEntry(key, state) {
|
|
5739
|
+
if (entries === null) {
|
|
5740
|
+
clonedBytes += SLOT_BYTES * module.entries.size;
|
|
5741
|
+
entries = new Map(module.entries);
|
|
5742
|
+
}
|
|
5743
|
+
entries.set(key, state);
|
|
5744
|
+
};
|
|
5745
|
+
/** Sets it, unless the entry already failed: an entry's error is sticky. */
|
|
5746
|
+
var settle = function settle(key, state) {
|
|
5747
|
+
var _stateOf;
|
|
5748
|
+
if (((_stateOf = stateOf(key)) === null || _stateOf === void 0 ? void 0 : _stateOf.kind) !== "error") setEntry(key, state);
|
|
5749
|
+
};
|
|
5750
|
+
/**
|
|
5751
|
+
* The record's keys as they stand while this patch is walked, for a write
|
|
5752
|
+
* of the whole record to expand against. Only the key set: the source holds
|
|
5753
|
+
* markers, and an entry's content may not be loaded. Seeded from the source
|
|
5754
|
+
* BEFORE this patch and kept current by the whole-entry ops below.
|
|
5755
|
+
*/
|
|
5756
|
+
var keys = null;
|
|
5757
|
+
var currentKeys = function currentKeys() {
|
|
5758
|
+
if (keys === null) {
|
|
5759
|
+
keys = new Map();
|
|
5760
|
+
if (isObjectNode(module.source)) {
|
|
5761
|
+
for (var _i = 0, _Object$keys = Object.keys(module.source); _i < _Object$keys.length; _i++) {
|
|
5762
|
+
var _key = _Object$keys[_i];
|
|
5763
|
+
keys.set(_key, undefined);
|
|
5764
|
+
}
|
|
5765
|
+
}
|
|
5766
|
+
}
|
|
5767
|
+
return keys;
|
|
5768
|
+
};
|
|
5769
|
+
/** Where a file's `patch_id` lands in the source; last write wins its place. */
|
|
5770
|
+
var fileMarks = new Map();
|
|
5771
|
+
/** And in an entry's content. */
|
|
5772
|
+
var entryFileMarks = [];
|
|
5773
|
+
var _iterator = _createForOfIteratorHelper(record.patch),
|
|
5774
|
+
_step;
|
|
5775
|
+
try {
|
|
5776
|
+
for (_iterator.s(); !(_step = _iterator.n()).done;) {
|
|
5777
|
+
var rawOp = _step.value;
|
|
5778
|
+
/*
|
|
5779
|
+
* A write of the WHOLE record fans out into per-entry ops first, through
|
|
5780
|
+
* the expansion the commit flow uses too (`expandJsonValuesRootOp`).
|
|
5781
|
+
* Left as it is, it would put content where the source holds markers.
|
|
5782
|
+
*/
|
|
5783
|
+
var ops = [rawOp];
|
|
5784
|
+
if (schema !== undefined && isJsonValuesRootOp(schema, rawOp)) {
|
|
5785
|
+
var expanded = expandJsonValuesRootOp(rawOp, currentKeys());
|
|
5786
|
+
if (result.isErr(expanded)) return fail(expanded.error.message);
|
|
5787
|
+
ops = expanded.value;
|
|
5788
|
+
}
|
|
5789
|
+
var _iterator3 = _createForOfIteratorHelper(ops),
|
|
5790
|
+
_step3;
|
|
5791
|
+
try {
|
|
5792
|
+
for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
|
|
5793
|
+
var _op = _step3.value;
|
|
5794
|
+
var cls = schema === undefined ? {
|
|
5795
|
+
kind: "normal"
|
|
5796
|
+
} : classifyJsonValuesOp(schema, _op.path);
|
|
5797
|
+
if (_op.op === "file") {
|
|
5798
|
+
var _op$nestedFilePath;
|
|
5799
|
+
/*
|
|
5800
|
+
* A file op carries bytes, not content. What the content needs is
|
|
5801
|
+
* the `patch_id` saying those bytes are not published yet, so the
|
|
5802
|
+
* file is served from the patch rather than from a `/public` path
|
|
5803
|
+
* that holds nothing. Keyed by where it LANDS -- the field's path
|
|
5804
|
+
* plus `nestedFilePath` -- because a video's poster and captions are
|
|
5805
|
+
* file ops on the video's own path. A delete has no bytes to point at.
|
|
5806
|
+
*/
|
|
5807
|
+
if (_op.value === null) continue;
|
|
5808
|
+
var nested = (_op$nestedFilePath = _op.nestedFilePath) !== null && _op$nestedFilePath !== void 0 ? _op$nestedFilePath : [];
|
|
5809
|
+
if (cls.kind === "entry" && cls.subPath.length > 0) {
|
|
5810
|
+
if (cls.recordPath.length === 0) {
|
|
5811
|
+
entryFileMarks.push({
|
|
5812
|
+
key: cls.entryKey,
|
|
5813
|
+
path: cls.subPath.concat(nested, "patch_id")
|
|
5814
|
+
});
|
|
5815
|
+
}
|
|
5816
|
+
continue;
|
|
5817
|
+
}
|
|
5818
|
+
var at = _op.path.concat(nested, "patch_id");
|
|
5819
|
+
fileMarks.set(JSON.stringify(at), at);
|
|
5820
|
+
if (cls.kind === "entry" && cls.recordPath.length === 0) {
|
|
5821
|
+
// The entry IS the media: its content gets the mark too.
|
|
5822
|
+
entryFileMarks.push({
|
|
5823
|
+
key: cls.entryKey,
|
|
5824
|
+
path: nested.concat("patch_id")
|
|
5825
|
+
});
|
|
5826
|
+
}
|
|
5827
|
+
continue;
|
|
5828
|
+
}
|
|
5829
|
+
if (cls.kind === "normal") {
|
|
5830
|
+
var _applied = applyOp(source, _op);
|
|
5831
|
+
if (result.isErr(_applied)) return fail(_applied.error.message);
|
|
5832
|
+
source = _applied.value;
|
|
5833
|
+
if (_op.op === "move") {
|
|
5834
|
+
var fromKey = wholeEntryKey(schema, _op.from);
|
|
5835
|
+
if (fromKey !== undefined) settle(fromKey, {
|
|
5836
|
+
kind: "deleted"
|
|
5837
|
+
});
|
|
5838
|
+
}
|
|
5839
|
+
if (_op.path.length === 0 && (schema === null || schema === void 0 ? void 0 : schema.type) === "record" && schema.jsonValues === true) {
|
|
5840
|
+
// The record itself was replaced (by something that is not a
|
|
5841
|
+
// record): every entry went with it.
|
|
5842
|
+
clonedBytes += SLOT_BYTES * (entries !== null && entries !== void 0 ? entries : module.entries).size;
|
|
5843
|
+
entries = new Map();
|
|
5844
|
+
}
|
|
5845
|
+
continue;
|
|
5846
|
+
}
|
|
5847
|
+
if (cls.recordPath.length > 0) {
|
|
5848
|
+
return fail("Nested .jsonValues() records are not supported: '".concat(cls.recordPath.join("."), "'. Use .jsonValues() only on a module's root record/router."));
|
|
5849
|
+
}
|
|
5850
|
+
var _key3 = cls.entryKey;
|
|
5851
|
+
if (cls.subPath.length > 0) {
|
|
5852
|
+
// An edit INSIDE an entry: its content, never the source's marker.
|
|
5853
|
+
if (_op.op === "move" || _op.op === "copy") {
|
|
5854
|
+
var fromCls = schema === undefined ? {
|
|
5855
|
+
kind: "normal"
|
|
5856
|
+
} : classifyJsonValuesOp(schema, _op.from);
|
|
5857
|
+
if (fromCls.kind !== "entry" || fromCls.entryKey !== _key3) {
|
|
5858
|
+
return fail("Cannot '".concat(_op.op, "' between different jsonValues entries"));
|
|
5859
|
+
}
|
|
5860
|
+
}
|
|
5861
|
+
var rebased = rebaseContentOp(_op, cls.recordPath.length + 1);
|
|
5862
|
+
if (result.isErr(rebased)) return fail(rebased.error.message);
|
|
5863
|
+
var _state = stateOf(_key3);
|
|
5864
|
+
if (_state === undefined || _state.kind === "deleted") {
|
|
5865
|
+
setEntry(_key3, {
|
|
5866
|
+
kind: "error",
|
|
5867
|
+
patchId: patchId,
|
|
5868
|
+
message: "Cannot edit jsonValues entry '".concat(_key3, "': it does not exist")
|
|
5869
|
+
});
|
|
5870
|
+
} else if (_state.kind === "waiting") {
|
|
5871
|
+
setEntry(_key3, {
|
|
5872
|
+
kind: "waiting",
|
|
5873
|
+
from: _state.from,
|
|
5874
|
+
ops: {
|
|
5875
|
+
op: rebased.value,
|
|
5876
|
+
patchId: patchId,
|
|
5877
|
+
before: _state.ops
|
|
5878
|
+
}
|
|
5879
|
+
});
|
|
5880
|
+
} else if (_state.kind === "content") {
|
|
5881
|
+
var _applied2 = applyOp(_state.content, rebased.value);
|
|
5882
|
+
setEntry(_key3, result.isErr(_applied2) ? {
|
|
5883
|
+
kind: "error",
|
|
5884
|
+
patchId: patchId,
|
|
5885
|
+
message: _applied2.error.message
|
|
5886
|
+
} : {
|
|
5887
|
+
kind: "content",
|
|
5888
|
+
content: _applied2.value
|
|
5889
|
+
});
|
|
5890
|
+
}
|
|
5891
|
+
continue;
|
|
5892
|
+
}
|
|
5893
|
+
|
|
5894
|
+
// A WHOLE entry: a marker in the source, content in the entry.
|
|
5895
|
+
var marker = _defineProperty(_defineProperty({}, VAL_EXTENSION, "json"), "patch_id", patchId);
|
|
5896
|
+
if (_op.op === "add" || _op.op === "replace") {
|
|
5897
|
+
var written = _op.op === "add" ? addAt(source, _op.path, marker) : replaceAt(source, _op.path, marker);
|
|
5898
|
+
if (result.isErr(written)) return fail(written.error.message);
|
|
5899
|
+
source = written.value;
|
|
5900
|
+
currentKeys().set(_key3, undefined);
|
|
5901
|
+
settle(_key3, {
|
|
5902
|
+
kind: "content",
|
|
5903
|
+
content: _op.value
|
|
5904
|
+
});
|
|
5905
|
+
} else if (_op.op === "remove") {
|
|
5906
|
+
var removed = removeAt(source, _op.path);
|
|
5907
|
+
if (result.isErr(removed)) return fail(removed.error.message);
|
|
5908
|
+
source = removed.value.doc;
|
|
5909
|
+
currentKeys()["delete"](_key3);
|
|
5910
|
+
settle(_key3, {
|
|
5911
|
+
kind: "deleted"
|
|
5912
|
+
});
|
|
5913
|
+
} else if (_op.op === "move" || _op.op === "copy") {
|
|
5914
|
+
var _fromCls = schema === undefined ? {
|
|
5915
|
+
kind: "normal"
|
|
5916
|
+
} : classifyJsonValuesOp(schema, _op.from);
|
|
5917
|
+
if (_fromCls.kind !== "entry" || _fromCls.subPath.length !== 0 || _fromCls.recordPath.length !== 0) {
|
|
5918
|
+
return fail("Cannot '".concat(_op.op, "' a jsonValues entry across records or from a non-entry path (from '").concat(_op.from.join("."), "' to '").concat(_op.path.join("."), "')"));
|
|
5919
|
+
}
|
|
5920
|
+
var _fromKey = _fromCls.entryKey;
|
|
5921
|
+
// Read before anything moves: the destination is the source entry
|
|
5922
|
+
// as it stood at this op.
|
|
5923
|
+
var fromState = stateOf(_fromKey);
|
|
5924
|
+
var added = addAt(source, _op.path, marker);
|
|
5925
|
+
if (result.isErr(added)) return fail(added.error.message);
|
|
5926
|
+
source = added.value;
|
|
5927
|
+
currentKeys().set(_key3, undefined);
|
|
5928
|
+
if (_op.op === "move") {
|
|
5929
|
+
var _removed = removeAt(source, _op.from);
|
|
5930
|
+
if (result.isErr(_removed)) return fail(_removed.error.message);
|
|
5931
|
+
source = _removed.value.doc;
|
|
5932
|
+
currentKeys()["delete"](_fromKey);
|
|
5933
|
+
}
|
|
5934
|
+
settle(_key3, fromState === undefined || fromState.kind === "deleted" ? {
|
|
5935
|
+
kind: "error",
|
|
5936
|
+
patchId: patchId,
|
|
5937
|
+
message: "Cannot resolve '".concat(_op.op, "' of jsonValues entry '").concat(_key3, "' from its own content")
|
|
5938
|
+
} : fromState);
|
|
5939
|
+
if (_op.op === "move" && _fromKey !== _key3) {
|
|
5940
|
+
settle(_fromKey, {
|
|
5941
|
+
kind: "deleted"
|
|
5942
|
+
});
|
|
5943
|
+
}
|
|
5944
|
+
} else {
|
|
5945
|
+
// `test` of a whole entry: the commit refuses it (`ValOps.prepare`),
|
|
5946
|
+
// so the read does too, rather than showing a patch publishing fails.
|
|
5947
|
+
return fail("Unsupported op '".concat(_op.op, "' on jsonValues entry '").concat(_key3, "' (supported: add, remove, replace, move, copy)"));
|
|
5948
|
+
}
|
|
5949
|
+
}
|
|
5950
|
+
} catch (err) {
|
|
5951
|
+
_iterator3.e(err);
|
|
5952
|
+
} finally {
|
|
5953
|
+
_iterator3.f();
|
|
5954
|
+
}
|
|
5955
|
+
}
|
|
5956
|
+
} catch (err) {
|
|
5957
|
+
_iterator.e(err);
|
|
5958
|
+
} finally {
|
|
5959
|
+
_iterator.f();
|
|
5960
|
+
}
|
|
5961
|
+
var _iterator2 = _createForOfIteratorHelper(fileMarks.values()),
|
|
5962
|
+
_step2;
|
|
5963
|
+
try {
|
|
5964
|
+
for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
|
|
5965
|
+
var _at = _step2.value;
|
|
5966
|
+
var marked = addAt(source, _at, patchId);
|
|
5967
|
+
if (result.isErr(marked)) return fail(marked.error.message);
|
|
5968
|
+
source = marked.value;
|
|
5969
|
+
}
|
|
5970
|
+
} catch (err) {
|
|
5971
|
+
_iterator2.e(err);
|
|
5972
|
+
} finally {
|
|
5973
|
+
_iterator2.f();
|
|
5974
|
+
}
|
|
5975
|
+
for (var _i2 = 0, _entryFileMarks = entryFileMarks; _i2 < _entryFileMarks.length; _i2++) {
|
|
5976
|
+
var _entryFileMarks$_i = _entryFileMarks[_i2],
|
|
5977
|
+
_key2 = _entryFileMarks$_i.key,
|
|
5978
|
+
path = _entryFileMarks$_i.path;
|
|
5979
|
+
var state = stateOf(_key2);
|
|
5980
|
+
var op = {
|
|
5981
|
+
op: "add",
|
|
5982
|
+
path: path,
|
|
5983
|
+
value: patchId
|
|
5984
|
+
};
|
|
5985
|
+
if ((state === null || state === void 0 ? void 0 : state.kind) === "content") {
|
|
5986
|
+
var applied = applyOp(state.content, op);
|
|
5987
|
+
setEntry(_key2, result.isErr(applied) ? {
|
|
5988
|
+
kind: "error",
|
|
5989
|
+
patchId: patchId,
|
|
5990
|
+
message: applied.error.message
|
|
5991
|
+
} : {
|
|
5992
|
+
kind: "content",
|
|
5993
|
+
content: applied.value
|
|
5994
|
+
});
|
|
5995
|
+
} else if ((state === null || state === void 0 ? void 0 : state.kind) === "waiting") {
|
|
5996
|
+
setEntry(_key2, {
|
|
5997
|
+
kind: "waiting",
|
|
5998
|
+
from: state.from,
|
|
5999
|
+
ops: {
|
|
6000
|
+
op: op,
|
|
6001
|
+
patchId: patchId,
|
|
6002
|
+
before: state.ops
|
|
6003
|
+
}
|
|
6004
|
+
});
|
|
6005
|
+
}
|
|
6006
|
+
}
|
|
6007
|
+
return {
|
|
6008
|
+
status: "applied",
|
|
6009
|
+
module: {
|
|
6010
|
+
source: source,
|
|
6011
|
+
entries: entries !== null && entries !== void 0 ? entries : module.entries
|
|
6012
|
+
}
|
|
6013
|
+
};
|
|
6014
|
+
}
|
|
6015
|
+
|
|
6016
|
+
/**
|
|
6017
|
+
* A module's chain: every patch in order, stopping at the first that does
|
|
6018
|
+
* not apply. `applied` is how many did, so the chain the content is AT is
|
|
6019
|
+
* the first `applied` ids -- what its version names.
|
|
6020
|
+
*/
|
|
6021
|
+
function applyChain(module, records, schema) {
|
|
6022
|
+
var current = module;
|
|
6023
|
+
var _iterator4 = _createForOfIteratorHelper(records.entries()),
|
|
6024
|
+
_step4;
|
|
6025
|
+
try {
|
|
6026
|
+
for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) {
|
|
6027
|
+
var _step4$value = _slicedToArray(_step4.value, 2),
|
|
6028
|
+
index = _step4$value[0],
|
|
6029
|
+
record = _step4$value[1];
|
|
6030
|
+
var res = applyPatchRecord(current, record, schema);
|
|
6031
|
+
if (res.status === "failed") {
|
|
6032
|
+
return {
|
|
6033
|
+
module: current,
|
|
6034
|
+
applied: index,
|
|
6035
|
+
failed: {
|
|
6036
|
+
patchId: res.patchId,
|
|
6037
|
+
message: res.message
|
|
6038
|
+
}
|
|
6039
|
+
};
|
|
6040
|
+
}
|
|
6041
|
+
current = res.module;
|
|
6042
|
+
}
|
|
6043
|
+
} catch (err) {
|
|
6044
|
+
_iterator4.e(err);
|
|
6045
|
+
} finally {
|
|
6046
|
+
_iterator4.f();
|
|
6047
|
+
}
|
|
6048
|
+
return {
|
|
6049
|
+
module: current,
|
|
6050
|
+
applied: records.length,
|
|
6051
|
+
failed: null
|
|
6052
|
+
};
|
|
6053
|
+
}
|
|
6054
|
+
|
|
6055
|
+
// #region entries
|
|
6056
|
+
|
|
6057
|
+
/**
|
|
6058
|
+
* What an entry is in this module: what the chain made it, or -- for one the
|
|
6059
|
+
* chain never touched -- the base's, if the source has its marker.
|
|
6060
|
+
*/
|
|
6061
|
+
function entryState(module, key) {
|
|
6062
|
+
var _module$entries$get;
|
|
6063
|
+
return (_module$entries$get = module.entries.get(key)) !== null && _module$entries$get !== void 0 ? _module$entries$get : implicitState(module.source, key);
|
|
6064
|
+
}
|
|
6065
|
+
function implicitState(source, key) {
|
|
6066
|
+
return isObjectNode(source) && Object.prototype.hasOwnProperty.call(source, key) && Internal.isJson(source[key]) ? {
|
|
6067
|
+
kind: "waiting",
|
|
6068
|
+
from: key,
|
|
6069
|
+
ops: null
|
|
6070
|
+
} : undefined;
|
|
6071
|
+
}
|
|
6072
|
+
|
|
6073
|
+
/** An entry's content, read. */
|
|
6074
|
+
|
|
6075
|
+
/**
|
|
6076
|
+
* An entry's content: what {@link entryState} says, with what it was waiting
|
|
6077
|
+
* for applied. `baseContentOf` is the BASE content of an entry by key (its
|
|
6078
|
+
* `*.val.json`, or what a save left there), `undefined` for one the base does
|
|
6079
|
+
* not have.
|
|
6080
|
+
*/
|
|
6081
|
+
function resolveEntry(key, state, baseContentOf) {
|
|
6082
|
+
if (state === undefined || state.kind === "deleted") {
|
|
6083
|
+
return {
|
|
6084
|
+
kind: "missing"
|
|
6085
|
+
};
|
|
6086
|
+
}
|
|
6087
|
+
if (state.kind === "error") {
|
|
6088
|
+
return {
|
|
6089
|
+
kind: "error",
|
|
6090
|
+
message: state.message,
|
|
6091
|
+
patchId: state.patchId
|
|
6092
|
+
};
|
|
6093
|
+
}
|
|
6094
|
+
if (state.kind === "content") {
|
|
6095
|
+
return {
|
|
6096
|
+
kind: "content",
|
|
6097
|
+
content: state.content
|
|
6098
|
+
};
|
|
6099
|
+
}
|
|
6100
|
+
var held = [];
|
|
6101
|
+
for (var at = state.ops; at !== null; at = at.before) held.push(at);
|
|
6102
|
+
var content = baseContentOf(state.from);
|
|
6103
|
+
if (content === undefined) {
|
|
6104
|
+
var first = held[held.length - 1];
|
|
6105
|
+
return first === undefined ? {
|
|
6106
|
+
kind: "missing"
|
|
6107
|
+
} : {
|
|
6108
|
+
kind: "error",
|
|
6109
|
+
message: "Cannot edit jsonValues entry '".concat(key, "': it does not exist"),
|
|
6110
|
+
patchId: first.patchId
|
|
6111
|
+
};
|
|
6112
|
+
}
|
|
6113
|
+
for (var i = held.length - 1; i >= 0; i--) {
|
|
6114
|
+
var _held$i = held[i],
|
|
6115
|
+
op = _held$i.op,
|
|
6116
|
+
patchId = _held$i.patchId;
|
|
6117
|
+
var applied = applyOp(content, op);
|
|
6118
|
+
if (result.isErr(applied)) {
|
|
6119
|
+
return {
|
|
6120
|
+
kind: "error",
|
|
6121
|
+
message: applied.error.message,
|
|
6122
|
+
patchId: patchId
|
|
6123
|
+
};
|
|
6124
|
+
}
|
|
6125
|
+
content = applied.value;
|
|
6126
|
+
}
|
|
6127
|
+
return {
|
|
6128
|
+
kind: "content",
|
|
6129
|
+
content: content
|
|
6130
|
+
};
|
|
6131
|
+
}
|
|
6132
|
+
|
|
6133
|
+
// #region copy-on-write JSON ops
|
|
6134
|
+
|
|
6135
|
+
/*
|
|
6136
|
+
* RFC 6902 as `JSONOps` (in `@valbuild/core/patch`) applies it -- the same
|
|
6137
|
+
* results and the same error messages, which the conformance cases hold --
|
|
6138
|
+
* but without mutating anything: a container on an op's path is copied, and
|
|
6139
|
+
* everything else is shared.
|
|
6140
|
+
*/
|
|
6141
|
+
|
|
6142
|
+
function isArrayNode(node) {
|
|
6143
|
+
return Array.isArray(node);
|
|
6144
|
+
}
|
|
6145
|
+
function isObjectNode(node) {
|
|
6146
|
+
return _typeof(node) === "object" && node !== null && !Array.isArray(node);
|
|
6147
|
+
}
|
|
6148
|
+
function hasOwn(node, key) {
|
|
6149
|
+
return Object.prototype.hasOwnProperty.call(node, key);
|
|
6150
|
+
}
|
|
6151
|
+
function patchError(message) {
|
|
6152
|
+
return result.err(new PatchError(message));
|
|
6153
|
+
}
|
|
6154
|
+
function copyArray(node) {
|
|
6155
|
+
clonedBytes += SLOT_BYTES * node.length;
|
|
6156
|
+
return node.slice();
|
|
6157
|
+
}
|
|
6158
|
+
|
|
6159
|
+
/**
|
|
6160
|
+
* A shallow copy, key by key. Not a spread, which was tried: a module's root
|
|
6161
|
+
* is a large object in dictionary mode, and V8 spreads one of forty thousand
|
|
6162
|
+
* keys at about half this speed (the 5 MB keystroke of
|
|
6163
|
+
* `scenarios/measure/keystroke.ts` went from 19 ms to 33).
|
|
6164
|
+
*/
|
|
6165
|
+
function copyObject(node) {
|
|
6166
|
+
var copy = {};
|
|
6167
|
+
var slots = 0;
|
|
6168
|
+
for (var _i3 = 0, _Object$keys2 = Object.keys(node); _i3 < _Object$keys2.length; _i3++) {
|
|
6169
|
+
var _key4 = _Object$keys2[_i3];
|
|
6170
|
+
setOwn(copy, _key4, node[_key4]);
|
|
6171
|
+
slots++;
|
|
6172
|
+
}
|
|
6173
|
+
clonedBytes += SLOT_BYTES * slots;
|
|
6174
|
+
return copy;
|
|
6175
|
+
}
|
|
6176
|
+
|
|
6177
|
+
/** An own property, even for `__proto__`, which `=` would not create. */
|
|
6178
|
+
function setOwn(target, key, value) {
|
|
6179
|
+
if (key === "__proto__") {
|
|
6180
|
+
Object.defineProperty(target, key, {
|
|
6181
|
+
value: value,
|
|
6182
|
+
writable: true,
|
|
6183
|
+
enumerable: true,
|
|
6184
|
+
configurable: true
|
|
6185
|
+
});
|
|
6186
|
+
} else {
|
|
6187
|
+
target[key] = value;
|
|
6188
|
+
}
|
|
6189
|
+
}
|
|
6190
|
+
function arrayIndex(key, node, inclusive) {
|
|
6191
|
+
if (inclusive && key === "-") return result.ok(node.length);
|
|
6192
|
+
var index = parseAndValidateArrayIndex(key);
|
|
6193
|
+
if (result.isErr(index)) return index;
|
|
6194
|
+
return (inclusive ? index.value <= node.length : index.value < node.length) ? index : patchError("Array index out of bounds");
|
|
6195
|
+
}
|
|
6196
|
+
function childOf(node, key) {
|
|
6197
|
+
if (isArrayNode(node)) {
|
|
6198
|
+
var index = parseAndValidateArrayIndex(key);
|
|
6199
|
+
if (result.isErr(index)) return index;
|
|
6200
|
+
return index.value >= node.length ? patchError("Array index out of bounds") : result.ok(node[index.value]);
|
|
6201
|
+
}
|
|
6202
|
+
if (isObjectNode(node)) {
|
|
6203
|
+
return result.ok(hasOwn(node, key) ? node[key] : undefined);
|
|
6204
|
+
}
|
|
6205
|
+
return patchError("Cannot access non-object/array");
|
|
6206
|
+
}
|
|
6207
|
+
|
|
6208
|
+
/** `node` with `key` set to `value`: the one copy a step on a path costs. */
|
|
6209
|
+
function withChild(node, key, value) {
|
|
6210
|
+
if (isArrayNode(node)) {
|
|
6211
|
+
var copy = copyArray(node);
|
|
6212
|
+
copy[Number(key)] = value;
|
|
6213
|
+
return copy;
|
|
6214
|
+
}
|
|
6215
|
+
if (isObjectNode(node)) {
|
|
6216
|
+
var _copy = copyObject(node);
|
|
6217
|
+
setOwn(_copy, key, value);
|
|
6218
|
+
return _copy;
|
|
6219
|
+
}
|
|
6220
|
+
return node;
|
|
6221
|
+
}
|
|
6222
|
+
|
|
6223
|
+
/**
|
|
6224
|
+
* Walks `path` to the parent of its last segment, lets `leaf` change that
|
|
6225
|
+
* parent, and copies every container on the way back up. Errors as
|
|
6226
|
+
* `JSONOps`'s pointer walk: the first missing step is reported.
|
|
6227
|
+
*/
|
|
6228
|
+
function updateAt(node, path, index, leaf) {
|
|
6229
|
+
var key = path[index];
|
|
6230
|
+
if (index === path.length - 1) return leaf(node, key);
|
|
6231
|
+
var child = childOf(node, key);
|
|
6232
|
+
if (result.isErr(child)) return child;
|
|
6233
|
+
if (child.value === undefined) {
|
|
6234
|
+
return patchError("Path refers to non-existing object/array");
|
|
6235
|
+
}
|
|
6236
|
+
var updated = updateAt(child.value, path, index + 1, leaf);
|
|
6237
|
+
if (result.isErr(updated)) return updated;
|
|
6238
|
+
return result.ok({
|
|
6239
|
+
node: updated.value.node === child.value ? node : withChild(node, key, updated.value.node),
|
|
6240
|
+
out: updated.value.out
|
|
6241
|
+
});
|
|
6242
|
+
}
|
|
6243
|
+
function isNonEmpty(path) {
|
|
6244
|
+
return path.length > 0;
|
|
6245
|
+
}
|
|
6246
|
+
function getAt(doc, path) {
|
|
6247
|
+
var node = doc;
|
|
6248
|
+
var _iterator5 = _createForOfIteratorHelper(path),
|
|
6249
|
+
_step5;
|
|
6250
|
+
try {
|
|
6251
|
+
for (_iterator5.s(); !(_step5 = _iterator5.n()).done;) {
|
|
6252
|
+
var _key5 = _step5.value;
|
|
6253
|
+
var child = childOf(node, _key5);
|
|
6254
|
+
if (result.isErr(child)) return child;
|
|
6255
|
+
if (child.value === undefined) {
|
|
6256
|
+
return patchError("Path refers to non-existing object/array");
|
|
6257
|
+
}
|
|
6258
|
+
node = child.value;
|
|
6259
|
+
}
|
|
6260
|
+
} catch (err) {
|
|
6261
|
+
_iterator5.e(err);
|
|
6262
|
+
} finally {
|
|
6263
|
+
_iterator5.f();
|
|
6264
|
+
}
|
|
6265
|
+
return result.ok(node);
|
|
6266
|
+
}
|
|
6267
|
+
function addAt(doc, path, value) {
|
|
6268
|
+
if (!isNonEmpty(path)) return result.ok(value);
|
|
6269
|
+
var res = updateAt(doc, path, 0, function (parent, key) {
|
|
6270
|
+
if (isArrayNode(parent)) {
|
|
6271
|
+
var index = arrayIndex(key, parent, true);
|
|
6272
|
+
if (result.isErr(index)) return index;
|
|
6273
|
+
var copy = copyArray(parent);
|
|
6274
|
+
copy.splice(index.value, 0, value);
|
|
6275
|
+
return result.ok({
|
|
6276
|
+
node: copy,
|
|
6277
|
+
out: null
|
|
6278
|
+
});
|
|
6279
|
+
}
|
|
6280
|
+
if (isObjectNode(parent)) {
|
|
6281
|
+
var _copy2 = copyObject(parent);
|
|
6282
|
+
setOwn(_copy2, key, value);
|
|
6283
|
+
return result.ok({
|
|
6284
|
+
node: _copy2,
|
|
6285
|
+
out: null
|
|
6286
|
+
});
|
|
6287
|
+
}
|
|
6288
|
+
return patchError("Cannot add to non-object/array");
|
|
6289
|
+
});
|
|
6290
|
+
return result.isErr(res) ? res : result.ok(res.value.node);
|
|
6291
|
+
}
|
|
6292
|
+
function replaceAt(doc, path, value) {
|
|
6293
|
+
if (!isNonEmpty(path)) return result.ok(value);
|
|
6294
|
+
var res = updateAt(doc, path, 0, function (parent, key) {
|
|
6295
|
+
if (isArrayNode(parent)) {
|
|
6296
|
+
var index = arrayIndex(key, parent, false);
|
|
6297
|
+
if (result.isErr(index)) return index;
|
|
6298
|
+
var copy = copyArray(parent);
|
|
6299
|
+
copy[index.value] = value;
|
|
6300
|
+
return result.ok({
|
|
6301
|
+
node: copy,
|
|
6302
|
+
out: null
|
|
6303
|
+
});
|
|
6304
|
+
}
|
|
6305
|
+
if (isObjectNode(parent)) {
|
|
6306
|
+
if (!hasOwn(parent, key)) {
|
|
6307
|
+
return patchError("Cannot replace object element which does not exist");
|
|
6308
|
+
}
|
|
6309
|
+
var _copy3 = copyObject(parent);
|
|
6310
|
+
setOwn(_copy3, key, value);
|
|
6311
|
+
return result.ok({
|
|
6312
|
+
node: _copy3,
|
|
6313
|
+
out: null
|
|
6314
|
+
});
|
|
6315
|
+
}
|
|
6316
|
+
return patchError("Cannot replace in non-object/array");
|
|
6317
|
+
});
|
|
6318
|
+
return result.isErr(res) ? res : result.ok(res.value.node);
|
|
6319
|
+
}
|
|
6320
|
+
function removeAt(doc, path) {
|
|
6321
|
+
if (!isNonEmpty(path)) {
|
|
6322
|
+
return patchError("Cannot remove from non-object/array");
|
|
6323
|
+
}
|
|
6324
|
+
var res = updateAt(doc, path, 0, function (parent, key) {
|
|
6325
|
+
if (isArrayNode(parent)) {
|
|
6326
|
+
var index = arrayIndex(key, parent, false);
|
|
6327
|
+
if (result.isErr(index)) return index;
|
|
6328
|
+
var copy = copyArray(parent);
|
|
6329
|
+
var _copy$splice = copy.splice(index.value, 1),
|
|
6330
|
+
_copy$splice2 = _slicedToArray(_copy$splice, 1),
|
|
6331
|
+
removed = _copy$splice2[0];
|
|
6332
|
+
return result.ok({
|
|
6333
|
+
node: copy,
|
|
6334
|
+
out: removed
|
|
6335
|
+
});
|
|
6336
|
+
}
|
|
6337
|
+
if (isObjectNode(parent) && hasOwn(parent, key)) {
|
|
6338
|
+
var _copy4 = copyObject(parent);
|
|
6339
|
+
var _removed2 = parent[key];
|
|
6340
|
+
delete _copy4[key];
|
|
6341
|
+
return result.ok({
|
|
6342
|
+
node: _copy4,
|
|
6343
|
+
out: _removed2
|
|
6344
|
+
});
|
|
6345
|
+
}
|
|
6346
|
+
return patchError("Cannot remove from non-object/array");
|
|
6347
|
+
});
|
|
6348
|
+
return result.isErr(res) ? res : result.ok({
|
|
6349
|
+
doc: res.value.node,
|
|
6350
|
+
removed: res.value.out
|
|
6351
|
+
});
|
|
6352
|
+
}
|
|
6353
|
+
|
|
6354
|
+
/** One op on a document. `file` is not an op on content: the caller marks. */
|
|
6355
|
+
function applyOp(doc, op) {
|
|
6356
|
+
switch (op.op) {
|
|
6357
|
+
case "add":
|
|
6358
|
+
return addAt(doc, op.path, op.value);
|
|
6359
|
+
case "replace":
|
|
6360
|
+
return replaceAt(doc, op.path, op.value);
|
|
6361
|
+
case "remove":
|
|
6362
|
+
{
|
|
6363
|
+
var removed = removeAt(doc, op.path);
|
|
6364
|
+
return result.isErr(removed) ? removed : result.ok(removed.value.doc);
|
|
6365
|
+
}
|
|
6366
|
+
case "move":
|
|
6367
|
+
{
|
|
6368
|
+
var _removed3 = removeAt(doc, op.from);
|
|
6369
|
+
if (result.isErr(_removed3)) return _removed3;
|
|
6370
|
+
return addAt(_removed3.value.doc, op.path, _removed3.value.removed);
|
|
6371
|
+
}
|
|
6372
|
+
case "copy":
|
|
6373
|
+
{
|
|
6374
|
+
// Shared, not cloned: nothing here mutates, so two places can hold one
|
|
6375
|
+
// value.
|
|
6376
|
+
var value = getAt(doc, op.from);
|
|
6377
|
+
if (result.isErr(value)) return value;
|
|
6378
|
+
return addAt(doc, op.path, value.value);
|
|
6379
|
+
}
|
|
6380
|
+
case "test":
|
|
6381
|
+
{
|
|
6382
|
+
// As `applyPatch` in `@valbuild/core/patch`, which the commit applies
|
|
6383
|
+
// `.val.ts` text through: a path that does not exist, or a value there
|
|
6384
|
+
// that is not deeply equal, fails the patch (RFC 6902, 4.6). The two
|
|
6385
|
+
// have to agree, or a draft shows a patch Publish refuses.
|
|
6386
|
+
var _value = getAt(doc, op.path);
|
|
6387
|
+
if (result.isErr(_value)) return _value;
|
|
6388
|
+
if (!deepEqual(op.value, _value.value)) return patchError("Test failed");
|
|
6389
|
+
return result.ok(doc);
|
|
6390
|
+
}
|
|
6391
|
+
case "file":
|
|
6392
|
+
return patchError("Cannot apply a file patch here");
|
|
6393
|
+
}
|
|
6394
|
+
}
|
|
6395
|
+
|
|
6396
|
+
/*
|
|
6397
|
+
* The store: modules by address. See `docs/plans/one-content-model.md`,
|
|
6398
|
+
* "The store: one address, immutable values, tiers".
|
|
6399
|
+
*
|
|
6400
|
+
* Content at a version never changes, so a store of modules by `moduleKey`
|
|
6401
|
+
* needs no invalidation, only eviction. A base is a version with the empty
|
|
6402
|
+
* chain, so the same store holds both. Every place that keeps modules uses
|
|
6403
|
+
* the same tiers, in the order that fits it: Val's server keeps
|
|
6404
|
+
* `[memoryTier, bundleTier, …]` in front of its head (`ValHead` in
|
|
6405
|
+
* `@valbuild/server`), and the Studio's will put an origin tier behind them
|
|
6406
|
+
* (phase 7).
|
|
6407
|
+
*/
|
|
6408
|
+
|
|
6409
|
+
/** One module's content at one version, as a tier holds it. */
|
|
6410
|
+
|
|
6411
|
+
// #region counted work
|
|
6412
|
+
|
|
6413
|
+
var tierHits = new Map();
|
|
6414
|
+
var tierMisses = 0;
|
|
6415
|
+
|
|
6416
|
+
/**
|
|
6417
|
+
* What the stores in this process have answered, by tier, and how often
|
|
6418
|
+
* none of them had a key: a test takes it before and after. Never reset.
|
|
6419
|
+
*/
|
|
6420
|
+
function storeWork() {
|
|
6421
|
+
return {
|
|
6422
|
+
hits: Object.fromEntries(tierHits),
|
|
6423
|
+
misses: tierMisses
|
|
6424
|
+
};
|
|
6425
|
+
}
|
|
6426
|
+
|
|
6427
|
+
// #region tiered
|
|
6428
|
+
|
|
6429
|
+
/**
|
|
6430
|
+
* Asks each tier in turn; what a slower tier answers is written to the faster
|
|
6431
|
+
* ones before it. A tier that answers `null` has no say.
|
|
6432
|
+
*/
|
|
6433
|
+
function tiered(tiers) {
|
|
6434
|
+
return {
|
|
6435
|
+
name: tiers.map(function (tier) {
|
|
6436
|
+
return tier.name;
|
|
6437
|
+
}).join(">"),
|
|
6438
|
+
get: function get(key) {
|
|
6439
|
+
return _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee() {
|
|
6440
|
+
var _iterator, _step, _tierHits$get, _step$value, index, tier, found, _iterator2, _step2, _faster$put, faster, _t;
|
|
6441
|
+
return _regenerator().w(function (_context) {
|
|
6442
|
+
while (1) switch (_context.p = _context.n) {
|
|
6443
|
+
case 0:
|
|
6444
|
+
_iterator = _createForOfIteratorHelper(tiers.entries());
|
|
6445
|
+
_context.p = 1;
|
|
6446
|
+
_iterator.s();
|
|
6447
|
+
case 2:
|
|
6448
|
+
if ((_step = _iterator.n()).done) {
|
|
6449
|
+
_context.n = 6;
|
|
6450
|
+
break;
|
|
6451
|
+
}
|
|
6452
|
+
_step$value = _slicedToArray(_step.value, 2), index = _step$value[0], tier = _step$value[1];
|
|
6453
|
+
_context.n = 3;
|
|
6454
|
+
return tier.get(key);
|
|
6455
|
+
case 3:
|
|
6456
|
+
found = _context.v;
|
|
6457
|
+
if (!(found === null)) {
|
|
6458
|
+
_context.n = 4;
|
|
6459
|
+
break;
|
|
6460
|
+
}
|
|
6461
|
+
return _context.a(3, 5);
|
|
6462
|
+
case 4:
|
|
6463
|
+
tierHits.set(tier.name, ((_tierHits$get = tierHits.get(tier.name)) !== null && _tierHits$get !== void 0 ? _tierHits$get : 0) + 1);
|
|
6464
|
+
_iterator2 = _createForOfIteratorHelper(tiers.slice(0, index));
|
|
6465
|
+
try {
|
|
6466
|
+
for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
|
|
6467
|
+
faster = _step2.value;
|
|
6468
|
+
(_faster$put = faster.put) === null || _faster$put === void 0 || _faster$put.call(faster, key, found, found.bytes);
|
|
6469
|
+
}
|
|
6470
|
+
} catch (err) {
|
|
6471
|
+
_iterator2.e(err);
|
|
6472
|
+
} finally {
|
|
6473
|
+
_iterator2.f();
|
|
6474
|
+
}
|
|
6475
|
+
return _context.a(2, found);
|
|
6476
|
+
case 5:
|
|
6477
|
+
_context.n = 2;
|
|
6478
|
+
break;
|
|
6479
|
+
case 6:
|
|
6480
|
+
_context.n = 8;
|
|
6481
|
+
break;
|
|
6482
|
+
case 7:
|
|
6483
|
+
_context.p = 7;
|
|
6484
|
+
_t = _context.v;
|
|
6485
|
+
_iterator.e(_t);
|
|
6486
|
+
case 8:
|
|
6487
|
+
_context.p = 8;
|
|
6488
|
+
_iterator.f();
|
|
6489
|
+
return _context.f(8);
|
|
6490
|
+
case 9:
|
|
6491
|
+
tierMisses++;
|
|
6492
|
+
return _context.a(2, null);
|
|
6493
|
+
}
|
|
6494
|
+
}, _callee, null, [[1, 7, 8, 9]]);
|
|
6495
|
+
}))();
|
|
6496
|
+
},
|
|
6497
|
+
put: function put(key, value, bytes) {
|
|
6498
|
+
var _tiers$, _tiers$$put;
|
|
6499
|
+
(_tiers$ = tiers[0]) === null || _tiers$ === void 0 || (_tiers$$put = _tiers$.put) === null || _tiers$$put === void 0 || _tiers$$put.call(_tiers$, key, value, bytes);
|
|
6500
|
+
}
|
|
6501
|
+
};
|
|
6502
|
+
}
|
|
6503
|
+
|
|
6504
|
+
// #region memory
|
|
6505
|
+
|
|
6506
|
+
/**
|
|
6507
|
+
* Bounded by bytes; keeps the last `lastPerModule` versions per module path
|
|
6508
|
+
* (and, apart from them, the last `lastPerModule` bases), and lets go of the least recently read value when over `budgetBytes`. A
|
|
6509
|
+
* value bigger than the whole budget is still held while it is the newest,
|
|
6510
|
+
* so the read that made it is answered; the next put lets it go.
|
|
6511
|
+
*/
|
|
6512
|
+
function memoryTier(opts) {
|
|
6513
|
+
var _opts$name;
|
|
6514
|
+
/** In least-recently-read order: a read moves a key to the end. */
|
|
6515
|
+
var held = new Map();
|
|
6516
|
+
/** Per module, its keys oldest first. */
|
|
6517
|
+
var rings = new Map();
|
|
6518
|
+
var bytes = 0;
|
|
6519
|
+
var evicted = 0;
|
|
6520
|
+
var versionOf = function versionOf(key) {
|
|
6521
|
+
var _held$get;
|
|
6522
|
+
return (_held$get = held.get(key)) === null || _held$get === void 0 ? void 0 : _held$get.value.version;
|
|
6523
|
+
};
|
|
6524
|
+
var drop = function drop(key) {
|
|
6525
|
+
var entry = held.get(key);
|
|
6526
|
+
if (entry === undefined) return;
|
|
6527
|
+
held["delete"](key);
|
|
6528
|
+
bytes -= entry.bytes;
|
|
6529
|
+
evicted++;
|
|
6530
|
+
var ring = rings.get(entry.value.path);
|
|
6531
|
+
if (ring !== undefined) {
|
|
6532
|
+
var at = ring.indexOf(key);
|
|
6533
|
+
if (at >= 0) ring.splice(at, 1);
|
|
6534
|
+
if (ring.length === 0) rings["delete"](entry.value.path);
|
|
6535
|
+
}
|
|
6536
|
+
};
|
|
6537
|
+
return {
|
|
6538
|
+
name: (_opts$name = opts.name) !== null && _opts$name !== void 0 ? _opts$name : "memory",
|
|
6539
|
+
get: function get(key) {
|
|
6540
|
+
var entry = held.get(key);
|
|
6541
|
+
if (entry === undefined) return null;
|
|
6542
|
+
held["delete"](key);
|
|
6543
|
+
held.set(key, entry);
|
|
6544
|
+
return entry.value;
|
|
6545
|
+
},
|
|
6546
|
+
put: function put(key, value, valueBytes) {
|
|
6547
|
+
var _rings$get;
|
|
6548
|
+
if (held.has(key)) {
|
|
6549
|
+
drop(key);
|
|
6550
|
+
evicted--;
|
|
6551
|
+
}
|
|
6552
|
+
held.set(key, {
|
|
6553
|
+
value: value,
|
|
6554
|
+
bytes: valueBytes
|
|
6555
|
+
});
|
|
6556
|
+
bytes += valueBytes;
|
|
6557
|
+
var ring = (_rings$get = rings.get(value.path)) !== null && _rings$get !== void 0 ? _rings$get : [];
|
|
6558
|
+
ring.push(key);
|
|
6559
|
+
rings.set(value.path, ring);
|
|
6560
|
+
// The last N versions, and apart from them the last N bases: a base
|
|
6561
|
+
// is what a slower tier answered (the bundle's own object, held
|
|
6562
|
+
// anyway) and written up here, and it must not push out the version
|
|
6563
|
+
// before the newest -- which a reader of the previous version still
|
|
6564
|
+
// asks for.
|
|
6565
|
+
var isBase = value.version.chain === EMPTY_CHAIN;
|
|
6566
|
+
var sameKind = ring.filter(function (held) {
|
|
6567
|
+
var _versionOf;
|
|
6568
|
+
return ((_versionOf = versionOf(held)) === null || _versionOf === void 0 ? void 0 : _versionOf.chain) === EMPTY_CHAIN === isBase;
|
|
6569
|
+
});
|
|
6570
|
+
var _iterator3 = _createForOfIteratorHelper(sameKind.slice(0, Math.max(0, sameKind.length - opts.lastPerModule))),
|
|
6571
|
+
_step3;
|
|
6572
|
+
try {
|
|
6573
|
+
for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
|
|
6574
|
+
var oldest = _step3.value;
|
|
6575
|
+
drop(oldest);
|
|
6576
|
+
}
|
|
6577
|
+
} catch (err) {
|
|
6578
|
+
_iterator3.e(err);
|
|
6579
|
+
} finally {
|
|
6580
|
+
_iterator3.f();
|
|
6581
|
+
}
|
|
6582
|
+
var _iterator4 = _createForOfIteratorHelper(held.keys()),
|
|
6583
|
+
_step4;
|
|
6584
|
+
try {
|
|
6585
|
+
for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) {
|
|
6586
|
+
var lru = _step4.value;
|
|
6587
|
+
if (bytes <= opts.budgetBytes) break;
|
|
6588
|
+
if (lru === key) continue;
|
|
6589
|
+
drop(lru);
|
|
6590
|
+
}
|
|
6591
|
+
} catch (err) {
|
|
6592
|
+
_iterator4.e(err);
|
|
6593
|
+
} finally {
|
|
6594
|
+
_iterator4.f();
|
|
6595
|
+
}
|
|
6596
|
+
},
|
|
6597
|
+
charge: function charge(key, extra) {
|
|
6598
|
+
var entry = held.get(key);
|
|
6599
|
+
if (entry === undefined) return;
|
|
6600
|
+
entry.bytes += extra;
|
|
6601
|
+
bytes += extra;
|
|
6602
|
+
},
|
|
6603
|
+
latest: function latest(path) {
|
|
6604
|
+
var _held$get$value, _held$get2;
|
|
6605
|
+
var ring = rings.get(path);
|
|
6606
|
+
var newest = ring === null || ring === void 0 ? void 0 : ring[ring.length - 1];
|
|
6607
|
+
return newest === undefined ? null : (_held$get$value = (_held$get2 = held.get(newest)) === null || _held$get2 === void 0 ? void 0 : _held$get2.value) !== null && _held$get$value !== void 0 ? _held$get$value : null;
|
|
6608
|
+
},
|
|
6609
|
+
stats: function stats() {
|
|
6610
|
+
// Versions: what a chain made. A base written up from a slower tier is
|
|
6611
|
+
// the build's own object (counted at no bytes), held beside them, and
|
|
6612
|
+
// not one of the module's versions.
|
|
6613
|
+
var mostVersions = 0;
|
|
6614
|
+
var mostBytes = 0;
|
|
6615
|
+
var versions = 0;
|
|
6616
|
+
var modules = 0;
|
|
6617
|
+
var _iterator5 = _createForOfIteratorHelper(rings.values()),
|
|
6618
|
+
_step5;
|
|
6619
|
+
try {
|
|
6620
|
+
for (_iterator5.s(); !(_step5 = _iterator5.n()).done;) {
|
|
6621
|
+
var ring = _step5.value;
|
|
6622
|
+
var made = ring.filter(function (key) {
|
|
6623
|
+
var _versionOf2;
|
|
6624
|
+
return ((_versionOf2 = versionOf(key)) === null || _versionOf2 === void 0 ? void 0 : _versionOf2.chain) !== EMPTY_CHAIN;
|
|
6625
|
+
});
|
|
6626
|
+
versions += made.length;
|
|
6627
|
+
if (made.length > 0) modules++;
|
|
6628
|
+
mostVersions = Math.max(mostVersions, made.length);
|
|
6629
|
+
var moduleBytes = 0;
|
|
6630
|
+
var _iterator6 = _createForOfIteratorHelper(ring),
|
|
6631
|
+
_step6;
|
|
6632
|
+
try {
|
|
6633
|
+
for (_iterator6.s(); !(_step6 = _iterator6.n()).done;) {
|
|
6634
|
+
var _held$get$bytes, _held$get3;
|
|
6635
|
+
var _key = _step6.value;
|
|
6636
|
+
moduleBytes += (_held$get$bytes = (_held$get3 = held.get(_key)) === null || _held$get3 === void 0 ? void 0 : _held$get3.bytes) !== null && _held$get$bytes !== void 0 ? _held$get$bytes : 0;
|
|
6637
|
+
}
|
|
6638
|
+
} catch (err) {
|
|
6639
|
+
_iterator6.e(err);
|
|
6640
|
+
} finally {
|
|
6641
|
+
_iterator6.f();
|
|
6642
|
+
}
|
|
6643
|
+
mostBytes = Math.max(mostBytes, moduleBytes);
|
|
6644
|
+
}
|
|
6645
|
+
} catch (err) {
|
|
6646
|
+
_iterator5.e(err);
|
|
6647
|
+
} finally {
|
|
6648
|
+
_iterator5.f();
|
|
6649
|
+
}
|
|
6650
|
+
return {
|
|
6651
|
+
bytes: bytes,
|
|
6652
|
+
versions: versions,
|
|
6653
|
+
modules: modules,
|
|
6654
|
+
mostVersionsOfOneModule: mostVersions,
|
|
6655
|
+
mostBytesOfOneModule: mostBytes,
|
|
6656
|
+
evicted: evicted
|
|
6657
|
+
};
|
|
6658
|
+
}
|
|
6659
|
+
};
|
|
6660
|
+
}
|
|
6661
|
+
|
|
6662
|
+
// #region bundle
|
|
6663
|
+
|
|
6664
|
+
/**
|
|
6665
|
+
* The build's own modules, free: answers only a key whose version is the
|
|
6666
|
+
* bundle's -- its schema, its base and the empty chain -- with the bundle's
|
|
6667
|
+
* own object, which the process holds for every visitor anyway. `bundle`
|
|
6668
|
+
* says what the build evaluated each module to; `null` for a module the
|
|
6669
|
+
* build does not have.
|
|
6670
|
+
*/
|
|
6671
|
+
function bundleTier(bundle) {
|
|
6672
|
+
var name = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : "bundle";
|
|
6673
|
+
return {
|
|
6674
|
+
name: name,
|
|
6675
|
+
get: function get(key) {
|
|
6676
|
+
return _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee2() {
|
|
6677
|
+
var path, own, version;
|
|
6678
|
+
return _regenerator().w(function (_context2) {
|
|
6679
|
+
while (1) switch (_context2.n) {
|
|
6680
|
+
case 0:
|
|
6681
|
+
path = pathOfModuleKey(key);
|
|
6682
|
+
if (!(path === null)) {
|
|
6683
|
+
_context2.n = 1;
|
|
6684
|
+
break;
|
|
6685
|
+
}
|
|
6686
|
+
return _context2.a(2, null);
|
|
6687
|
+
case 1:
|
|
6688
|
+
_context2.n = 2;
|
|
6689
|
+
return bundle(path);
|
|
6690
|
+
case 2:
|
|
6691
|
+
own = _context2.v;
|
|
6692
|
+
if (!(own === null)) {
|
|
6693
|
+
_context2.n = 3;
|
|
6694
|
+
break;
|
|
6695
|
+
}
|
|
6696
|
+
return _context2.a(2, null);
|
|
6697
|
+
case 3:
|
|
6698
|
+
version = _objectSpread2(_objectSpread2({}, own.version), {}, {
|
|
6699
|
+
chain: EMPTY_CHAIN
|
|
6700
|
+
});
|
|
6701
|
+
if (!(moduleKey(path, version) !== key)) {
|
|
6702
|
+
_context2.n = 4;
|
|
6703
|
+
break;
|
|
6704
|
+
}
|
|
6705
|
+
return _context2.a(2, null);
|
|
6706
|
+
case 4:
|
|
6707
|
+
return _context2.a(2, {
|
|
6708
|
+
path: path,
|
|
6709
|
+
version: version,
|
|
6710
|
+
content: baseModuleContent(own.source),
|
|
6711
|
+
patches: [],
|
|
6712
|
+
bytes: 0
|
|
6713
|
+
});
|
|
6714
|
+
}
|
|
6715
|
+
}, _callee2);
|
|
6716
|
+
}))();
|
|
6717
|
+
}
|
|
6718
|
+
};
|
|
6719
|
+
}
|
|
6720
|
+
|
|
6721
|
+
/** The module a {@link moduleKey} is the address of, or `null` for none. */
|
|
6722
|
+
function pathOfModuleKey(key) {
|
|
6723
|
+
var at = key.lastIndexOf("@");
|
|
6724
|
+
return at <= 0 ? null : key.slice(0, at);
|
|
6725
|
+
}
|
|
6726
|
+
|
|
6727
|
+
/**
|
|
6728
|
+
* The version a {@link moduleKey} names, or `null` when it is not one. The
|
|
6729
|
+
* three parts are hex or empty, so the split is unambiguous.
|
|
6730
|
+
*/
|
|
6731
|
+
function parseModuleKey(key) {
|
|
6732
|
+
var path = pathOfModuleKey(key);
|
|
6733
|
+
if (path === null) return null;
|
|
6734
|
+
var parts = key.slice(path.length + 1).split(".");
|
|
6735
|
+
var _parts = _slicedToArray(parts, 3),
|
|
6736
|
+
schema = _parts[0],
|
|
6737
|
+
base = _parts[1],
|
|
6738
|
+
chain = _parts[2];
|
|
6739
|
+
if (parts.length !== 3 || schema === undefined || base === undefined || chain === undefined) {
|
|
6740
|
+
return null;
|
|
6741
|
+
}
|
|
6742
|
+
return {
|
|
6743
|
+
path: path,
|
|
6744
|
+
version: {
|
|
6745
|
+
schema: schema,
|
|
6746
|
+
base: base,
|
|
6747
|
+
chain: chain
|
|
6748
|
+
}
|
|
6749
|
+
};
|
|
6750
|
+
}
|
|
6751
|
+
|
|
6752
|
+
// #region entries
|
|
6753
|
+
|
|
6754
|
+
/**
|
|
6755
|
+
* A `.jsonValues()` entry's version: the record's schema and base, the
|
|
6756
|
+
* entry's key, and the chain of the record's patches that touched THIS
|
|
6757
|
+
* entry -- so an entry no patch touched keeps its version while the record's
|
|
6758
|
+
* chain grows, and two reads at one entry version are the same content.
|
|
6759
|
+
* Entries are stored on their own, so a large record is not copied because
|
|
6760
|
+
* one entry changed.
|
|
6761
|
+
*/
|
|
6762
|
+
|
|
6763
|
+
/** The address of an entry's content. Never changes meaning. */
|
|
6764
|
+
function entryAddress(path, v) {
|
|
6765
|
+
return "".concat(moduleKey(path, v), "#").concat(v.key);
|
|
6766
|
+
}
|
|
6767
|
+
|
|
6768
|
+
/** Whether two entry versions name the same content. */
|
|
6769
|
+
function sameEntryVersion(a, b) {
|
|
6770
|
+
return a.key === b.key && a.schema === b.schema && a.base === b.base && a.chain === b.chain;
|
|
6771
|
+
}
|
|
6772
|
+
|
|
6773
|
+
var SharedValConfig = z.object({
|
|
6774
|
+
contentHostUrl: z.string().optional(),
|
|
6775
|
+
appHostUrl: z.string().optional(),
|
|
6776
|
+
project: z.string().optional(),
|
|
4753
6777
|
root: z.string().optional(),
|
|
4754
6778
|
gitCommit: z.string().optional(),
|
|
4755
6779
|
gitBranch: z.string().optional(),
|
|
@@ -6230,24 +8254,30 @@ function emptyRecord(schema, context) {
|
|
|
6230
8254
|
}));
|
|
6231
8255
|
}
|
|
6232
8256
|
|
|
6233
|
-
var toolNames = ["get_all_schema", "get_source", "search_content", "validate_content", "create_patch", "add_session_image_to_gallery", "remove_image_gallery_entry", "navigate_to", "get_current_context", "get_patches", "get_source_path_from_route", "set_session_name", "show_compare_view", "ask_user_question", "duplicate_source", "empty_at_path", "count_entries", "get_record_keys", "list_sketchpads", "create_sketchpad", "read_sketchpad", "write_sketchpad"];
|
|
8257
|
+
var toolNames = ["get_all_schema", "get_source", "search_content", "validate_content", "create_patch", "add_session_image_to_gallery", "remove_image_gallery_entry", "convert_session_image", "navigate_to", "get_current_context", "get_patches", "get_source_path_from_route", "set_session_name", "show_compare_view", "ask_user_question", "duplicate_source", "empty_at_path", "count_entries", "get_record_keys", "list_sketchpads", "create_sketchpad", "read_sketchpad", "write_sketchpad", "highlight_sketchpad_blocks", "clear_sketchpad_highlights"];
|
|
6234
8258
|
|
|
6235
8259
|
/**
|
|
6236
8260
|
* A page sketch: a page written as indented text, with four primitives.
|
|
6237
8261
|
*
|
|
6238
8262
|
* ```
|
|
8263
|
+
* --- /about a route: what follows is the page at /about
|
|
6239
8264
|
* # Our people text (a heading: #, ## or ###)
|
|
6240
8265
|
* We are 40 people in Oslo. text (a paragraph)
|
|
8266
|
+
* ## Hi {field: /x.val.ts?p="t"} text linked to a content field: typing
|
|
8267
|
+
* in it writes the field
|
|
6241
8268
|
* [image: Team photo] placeholder: [kind] or [kind: label]
|
|
6242
8269
|
* [hero: Roof](image: /a.jpg) a placeholder with an image (or video) in it
|
|
6243
8270
|
* icon hero M3 5h18v14H3z how every [hero] box is drawn: SVG path data
|
|
6244
8271
|
* row its children side by side
|
|
6245
|
-
* col
|
|
6246
|
-
* ## Design
|
|
8272
|
+
* col its children stacked; columns share a row
|
|
8273
|
+
* ## Design equally
|
|
6247
8274
|
* col
|
|
6248
8275
|
* [button: Join us]
|
|
6249
8276
|
* ```
|
|
6250
8277
|
*
|
|
8278
|
+
* A sketchpad is not one page's: a route divider starts the part about a
|
|
8279
|
+
* page, so one sketch can cover a page and the two it links to.
|
|
8280
|
+
*
|
|
6251
8281
|
* It is a scratch pad, not a layout language: it says WHAT is on a page and
|
|
6252
8282
|
* roughly WHERE, so an assistant and a person can agree on a page's content
|
|
6253
8283
|
* before anyone worries about how it looks. The person never sees this text,
|
|
@@ -6266,7 +8296,21 @@ var toolNames = ["get_all_schema", "get_source", "search_content", "validate_con
|
|
|
6266
8296
|
* that builds the page reads it exactly as the person left it.
|
|
6267
8297
|
*/
|
|
6268
8298
|
|
|
8299
|
+
/** The start of the part about one page. Only at the top level. */
|
|
8300
|
+
|
|
8301
|
+
/** Columns share their row equally: a sketch says what, not how wide. */
|
|
8302
|
+
|
|
8303
|
+
/**
|
|
8304
|
+
* `src` names a file the project has, never a guess: its `path` exactly as
|
|
8305
|
+
* the content has it (`/public/val/hero.webp`, or its remote URL), or an
|
|
8306
|
+
* image uploaded to the assistant's session, `session:<key>`. Which files
|
|
8307
|
+
* those are is the Studio's to check; the text only holds the name.
|
|
8308
|
+
*/
|
|
8309
|
+
|
|
8310
|
+
/** The prefix of a `src` that is an image in the assistant's session. */
|
|
8311
|
+
var SESSION_MEDIA_PREFIX = "session:";
|
|
6269
8312
|
var CONTAINER = /^(row|col)(?:\s+(\d+))?$/;
|
|
8313
|
+
var ROUTE = /^---(?:\s+(.*))?$/;
|
|
6270
8314
|
var PLACEHOLDER = /^\[([^\]:]*)(?::([^\]]*))?\](?:\((image|video):\s*([^\s)]+)\s*\))?$/;
|
|
6271
8315
|
var HAS_MEDIA = /^\[[^\]]*\]\(/;
|
|
6272
8316
|
var ICON = /^icon\s+(\S+)\s+([Mm][\d\s.-].*)$/;
|
|
@@ -6309,6 +8353,32 @@ function parseSketch(source) {
|
|
|
6309
8353
|
while (stack.length > 1 && indent <= stack[stack.length - 1].indent) {
|
|
6310
8354
|
stack.pop();
|
|
6311
8355
|
}
|
|
8356
|
+
var route = ROUTE.exec(content);
|
|
8357
|
+
if (route) {
|
|
8358
|
+
var _route$;
|
|
8359
|
+
var _path = ((_route$ = route[1]) !== null && _route$ !== void 0 ? _route$ : "").trim();
|
|
8360
|
+
if (!_path.startsWith("/") || /\s/.test(_path)) {
|
|
8361
|
+
errors.push({
|
|
8362
|
+
line: line,
|
|
8363
|
+
message: 'A route divider names one page by its route, like "--- /about".'
|
|
8364
|
+
});
|
|
8365
|
+
}
|
|
8366
|
+
if (indent > 0) {
|
|
8367
|
+
errors.push({
|
|
8368
|
+
line: line,
|
|
8369
|
+
message: "A route divider starts a page, so it goes at the top level, not indented."
|
|
8370
|
+
});
|
|
8371
|
+
}
|
|
8372
|
+
// It closes everything open: what follows is another page.
|
|
8373
|
+
stack.length = 1;
|
|
8374
|
+
stack[0].lastIndent = 0;
|
|
8375
|
+
blocks.push({
|
|
8376
|
+
type: "route",
|
|
8377
|
+
line: line,
|
|
8378
|
+
route: _path
|
|
8379
|
+
});
|
|
8380
|
+
return;
|
|
8381
|
+
}
|
|
6312
8382
|
var parent = stack[stack.length - 1];
|
|
6313
8383
|
var node = parseContent(content, line, errors);
|
|
6314
8384
|
|
|
@@ -6350,7 +8420,27 @@ function parseSketch(source) {
|
|
|
6350
8420
|
errors: errors
|
|
6351
8421
|
};
|
|
6352
8422
|
}
|
|
6353
|
-
|
|
8423
|
+
|
|
8424
|
+
/** The link to a content field at the end of a text line. */
|
|
8425
|
+
var FIELD = / \{field: (\/[^{}]*)\}$/;
|
|
8426
|
+
function parseContent(whole, line, errors) {
|
|
8427
|
+
var link = FIELD.exec(whole);
|
|
8428
|
+
if (link) {
|
|
8429
|
+
var node = parseLine(whole.slice(0, link.index), line, errors);
|
|
8430
|
+
if (node.type !== "text") {
|
|
8431
|
+
errors.push({
|
|
8432
|
+
line: line,
|
|
8433
|
+
message: "Only a heading or a paragraph is linked to a field: put {field: ...} at the end of one."
|
|
8434
|
+
});
|
|
8435
|
+
return node;
|
|
8436
|
+
}
|
|
8437
|
+
return _objectSpread2(_objectSpread2({}, node), {}, {
|
|
8438
|
+
field: link[1].trim()
|
|
8439
|
+
});
|
|
8440
|
+
}
|
|
8441
|
+
return parseLine(whole, line, errors);
|
|
8442
|
+
}
|
|
8443
|
+
function parseLine(content, line, errors) {
|
|
6354
8444
|
// A leading backslash keeps a line that looks like a keyword as text.
|
|
6355
8445
|
if (content.startsWith("\\")) {
|
|
6356
8446
|
return {
|
|
@@ -6375,17 +8465,15 @@ function parseContent(content, line, errors) {
|
|
|
6375
8465
|
children: []
|
|
6376
8466
|
};
|
|
6377
8467
|
}
|
|
6378
|
-
|
|
6379
|
-
if (weight === 0) {
|
|
8468
|
+
if (container[2] !== undefined) {
|
|
6380
8469
|
errors.push({
|
|
6381
8470
|
line: line,
|
|
6382
|
-
message:
|
|
8471
|
+
message: 'Columns share a row equally, so a col takes no number: write "col".'
|
|
6383
8472
|
});
|
|
6384
8473
|
}
|
|
6385
8474
|
return {
|
|
6386
8475
|
type: "col",
|
|
6387
8476
|
line: line,
|
|
6388
|
-
weight: Math.max(weight, 1),
|
|
6389
8477
|
children: []
|
|
6390
8478
|
};
|
|
6391
8479
|
}
|
|
@@ -6459,13 +8547,19 @@ function parseContent(content, line, errors) {
|
|
|
6459
8547
|
* The line a text block is written as. Line breaks become spaces, and a
|
|
6460
8548
|
* paragraph that would read as something else is kept as text with `\`.
|
|
6461
8549
|
*/
|
|
6462
|
-
function formatSketchText(level, text) {
|
|
8550
|
+
function formatSketchText(level, text, field) {
|
|
6463
8551
|
var flat = text.replace(/\s+/g, " ").trim();
|
|
8552
|
+
var path = field === null || field === void 0 ? void 0 : field.replace(/\s+/g, " ").trim();
|
|
8553
|
+
// A source path, and one the line can be read back with: no braces.
|
|
8554
|
+
var link = path && /^\/[^{}]*$/.test(path) ? " {field: ".concat(path, "}") : "";
|
|
6464
8555
|
if (level > 0) {
|
|
6465
|
-
return "#".repeat(level) + " " + flat;
|
|
8556
|
+
return "#".repeat(level) + " " + flat + link;
|
|
6466
8557
|
}
|
|
6467
|
-
|
|
6468
|
-
|
|
8558
|
+
// Read as the whole line will be, link and all: `icon x M` is text, and
|
|
8559
|
+
// `icon x M {field: ...}` would be an icon.
|
|
8560
|
+
var whole = flat + link;
|
|
8561
|
+
var ambiguous = flat.startsWith("\\") || flat.startsWith("[") || CONTAINER.test(whole) || ROUTE.test(whole) || HEADING.test(whole) || ICON.test(whole);
|
|
8562
|
+
return (ambiguous ? "\\" + flat : flat) + link;
|
|
6469
8563
|
}
|
|
6470
8564
|
|
|
6471
8565
|
/** The line a placeholder is written as. */
|
|
@@ -6490,17 +8584,26 @@ function formatSketchBox(placeholder) {
|
|
|
6490
8584
|
*/
|
|
6491
8585
|
function formatSketch(blocks, icons) {
|
|
6492
8586
|
var lines = [];
|
|
8587
|
+
// One block per field: the first keeps the link.
|
|
8588
|
+
var linked = new Set();
|
|
6493
8589
|
var _walk = function walk(node, depth, inRow) {
|
|
6494
8590
|
var indent = " ".repeat(depth);
|
|
6495
8591
|
switch (node.type) {
|
|
6496
8592
|
case "text":
|
|
6497
8593
|
if (node.text) {
|
|
6498
|
-
|
|
8594
|
+
var field = node.field && !linked.has(node.field) ? node.field : undefined;
|
|
8595
|
+
if (field) {
|
|
8596
|
+
linked.add(field);
|
|
8597
|
+
}
|
|
8598
|
+
lines.push(indent + formatSketchText(node.level, node.text, field));
|
|
6499
8599
|
}
|
|
6500
8600
|
return;
|
|
6501
8601
|
case "placeholder":
|
|
6502
8602
|
lines.push(indent + formatSketchBox(node));
|
|
6503
8603
|
return;
|
|
8604
|
+
case "route":
|
|
8605
|
+
lines.push("--- ".concat(node.route));
|
|
8606
|
+
return;
|
|
6504
8607
|
case "row":
|
|
6505
8608
|
lines.push(indent + "row");
|
|
6506
8609
|
if (node.children.length === 0) {
|
|
@@ -6517,7 +8620,7 @@ function formatSketch(blocks, icons) {
|
|
|
6517
8620
|
});
|
|
6518
8621
|
return;
|
|
6519
8622
|
}
|
|
6520
|
-
lines.push(indent +
|
|
8623
|
+
lines.push(indent + "col");
|
|
6521
8624
|
node.children.forEach(function (child) {
|
|
6522
8625
|
return _walk(child, depth + 1, false);
|
|
6523
8626
|
});
|
|
@@ -6537,6 +8640,33 @@ function formatSketch(blocks, icons) {
|
|
|
6537
8640
|
return all.length > 0 ? all.join("\n") + "\n" : "";
|
|
6538
8641
|
}
|
|
6539
8642
|
|
|
8643
|
+
/**
|
|
8644
|
+
* The text of every block linked to a field, by field: what the Studio
|
|
8645
|
+
* writes to the content when a linked block changes. The first block wins,
|
|
8646
|
+
* as `formatSketch` keeps only its link.
|
|
8647
|
+
*/
|
|
8648
|
+
function sketchFieldTexts(blocks) {
|
|
8649
|
+
var texts = new Map();
|
|
8650
|
+
var _walk2 = function walk(node) {
|
|
8651
|
+
if (node.type === "text") {
|
|
8652
|
+
if (node.field && node.text && !texts.has(node.field)) {
|
|
8653
|
+
texts.set(node.field, node.text.replace(/\s+/g, " ").trim());
|
|
8654
|
+
}
|
|
8655
|
+
} else if (node.type === "row" || node.type === "col") {
|
|
8656
|
+
node.children.forEach(_walk2);
|
|
8657
|
+
}
|
|
8658
|
+
};
|
|
8659
|
+
blocks.forEach(_walk2);
|
|
8660
|
+
return texts;
|
|
8661
|
+
}
|
|
8662
|
+
|
|
8663
|
+
/** The pages a sketch is about, in the order it gives them. */
|
|
8664
|
+
function sketchRoutes(blocks) {
|
|
8665
|
+
return blocks.flatMap(function (block) {
|
|
8666
|
+
return block.type === "route" && block.route !== "" ? [block.route] : [];
|
|
8667
|
+
});
|
|
8668
|
+
}
|
|
8669
|
+
|
|
6540
8670
|
/**
|
|
6541
8671
|
* What a list names a sketch by: its first top-level heading, the biggest
|
|
6542
8672
|
* one first. Null for a sketch with no heading, which a list then names by
|
|
@@ -7962,7 +10092,9 @@ function createSearchIndex() {
|
|
|
7962
10092
|
tokenize: "forward"
|
|
7963
10093
|
}),
|
|
7964
10094
|
pathToLabel: new Map(),
|
|
7965
|
-
docsByModule: new Map()
|
|
10095
|
+
docsByModule: new Map(),
|
|
10096
|
+
walkedByModule: new Map(),
|
|
10097
|
+
entriesByModule: new Map()
|
|
7966
10098
|
};
|
|
7967
10099
|
}
|
|
7968
10100
|
|
|
@@ -7976,77 +10108,230 @@ function createSearchIndex() {
|
|
|
7976
10108
|
* `.jsonValues()` entries are indexed only once their content is loaded — an
|
|
7977
10109
|
* un-loaded entry is an opaque marker that {@link traverseSchemaSource} skips, so
|
|
7978
10110
|
* the index is naturally PARTIAL and grows as batches land. That is why the search
|
|
7979
|
-
* UI loads the entries on the first query and re-indexes
|
|
7980
|
-
*
|
|
10111
|
+
* UI loads the entries on the first query and re-indexes as they arrive, rather
|
|
10112
|
+
* than pretending an empty result set is an answer -- and why a module whose
|
|
10113
|
+
* root is a record is re-indexed by entry ({@link indexRecordModule}): a pass
|
|
10114
|
+
* per batch walks the batch, not the record.
|
|
7981
10115
|
*/
|
|
7982
|
-
function indexModule(searchIndex, moduleFilePath, source, schema
|
|
7983
|
-
|
|
7984
|
-
|
|
7985
|
-
|
|
10116
|
+
function indexModule(searchIndex, moduleFilePath, source, schema,
|
|
10117
|
+
/**
|
|
10118
|
+
* Stop by this time (`performance.now()`), for a caller that must not hold
|
|
10119
|
+
* the thread: answers `false` when it stopped with entries of a record
|
|
10120
|
+
* module still to walk, and the same call again goes on from there. A
|
|
10121
|
+
* module that is not a record is walked whole, whatever the time.
|
|
10122
|
+
*/
|
|
10123
|
+
until) {
|
|
10124
|
+
var walked = searchIndex.walkedByModule.get(moduleFilePath);
|
|
10125
|
+
if (walked !== undefined && walked.source === source && walked.schema === schemaKeyOf(schema)) {
|
|
10126
|
+
return true;
|
|
10127
|
+
}
|
|
10128
|
+
if (schema.type === "record" && isJsonRecord(source)) {
|
|
10129
|
+
var done = indexRecordModule(searchIndex, moduleFilePath, source, schema, until);
|
|
10130
|
+
if (done) {
|
|
10131
|
+
searchIndex.walkedByModule.set(moduleFilePath, {
|
|
10132
|
+
source: source,
|
|
10133
|
+
schema: schemaKeyOf(schema)
|
|
10134
|
+
});
|
|
10135
|
+
}
|
|
10136
|
+
return done;
|
|
10137
|
+
}
|
|
7986
10138
|
removeModule(searchIndex, moduleFilePath);
|
|
7987
10139
|
var docs = new Set();
|
|
7988
|
-
docsByModule.set(moduleFilePath, docs);
|
|
10140
|
+
searchIndex.docsByModule.set(moduleFilePath, docs);
|
|
7989
10141
|
var path = moduleFilePath + "?p=";
|
|
7990
|
-
traverseSchemaSource(source, schema, path, function (
|
|
7991
|
-
var
|
|
7992
|
-
|
|
7993
|
-
|
|
7994
|
-
|
|
7995
|
-
|
|
7996
|
-
|
|
7997
|
-
|
|
7998
|
-
|
|
7999
|
-
|
|
8000
|
-
|
|
8001
|
-
|
|
8002
|
-
|
|
8003
|
-
|
|
8004
|
-
|
|
8005
|
-
|
|
8006
|
-
|
|
8007
|
-
|
|
8008
|
-
|
|
8009
|
-
|
|
8010
|
-
|
|
8011
|
-
|
|
8012
|
-
|
|
8013
|
-
|
|
8014
|
-
|
|
8015
|
-
|
|
8016
|
-
|
|
8017
|
-
|
|
8018
|
-
|
|
8019
|
-
|
|
8020
|
-
|
|
8021
|
-
|
|
8022
|
-
|
|
8023
|
-
|
|
8024
|
-
|
|
8025
|
-
|
|
8026
|
-
|
|
8027
|
-
|
|
8028
|
-
|
|
8029
|
-
|
|
8030
|
-
|
|
8031
|
-
|
|
8032
|
-
|
|
8033
|
-
|
|
8034
|
-
|
|
8035
|
-
|
|
10142
|
+
traverseSchemaSource(source, schema, path, function (leaf) {
|
|
10143
|
+
var id = indexLeaf(searchIndex, leaf);
|
|
10144
|
+
if (id !== null) docs.add(id);
|
|
10145
|
+
});
|
|
10146
|
+
searchIndex.walkedByModule.set(moduleFilePath, {
|
|
10147
|
+
source: source,
|
|
10148
|
+
schema: schemaKeyOf(schema)
|
|
10149
|
+
});
|
|
10150
|
+
return true;
|
|
10151
|
+
}
|
|
10152
|
+
|
|
10153
|
+
/** How many entries are walked between two looks at the clock. */
|
|
10154
|
+
var ENTRIES_PER_CLOCK_CHECK = 32;
|
|
10155
|
+
|
|
10156
|
+
/**
|
|
10157
|
+
* A module whose root is a record, indexed entry by entry: an entry whose
|
|
10158
|
+
* value is the SAME OBJECT it was indexed from keeps its documents, and only
|
|
10159
|
+
* the others are walked.
|
|
10160
|
+
*
|
|
10161
|
+
* What makes loading a `.jsonValues()` record of 10,000 entries searchable
|
|
10162
|
+
* while it loads. The Studio asks again as each batch lands, and the record's
|
|
10163
|
+
* value is copy-on-write -- an entry nothing touched is the object it was --
|
|
10164
|
+
* so each pass indexes the batch that arrived, not the record. Walked whole,
|
|
10165
|
+
* that was the whole record per batch: quadratic, and a long task each time.
|
|
10166
|
+
*
|
|
10167
|
+
* Identity is only ever a shortcut: a value that is equal but a new object
|
|
10168
|
+
* (a structured clone across a worker, a module read again) is walked again,
|
|
10169
|
+
* never skipped. A different schema starts the module over.
|
|
10170
|
+
*/
|
|
10171
|
+
function indexRecordModule(searchIndex, moduleFilePath, source, schema, until) {
|
|
10172
|
+
var index = searchIndex.index,
|
|
10173
|
+
pathToLabel = searchIndex.pathToLabel,
|
|
10174
|
+
docsByModule = searchIndex.docsByModule,
|
|
10175
|
+
entriesByModule = searchIndex.entriesByModule;
|
|
10176
|
+
var indexed = entriesByModule.get(moduleFilePath);
|
|
10177
|
+
var docs = docsByModule.get(moduleFilePath);
|
|
10178
|
+
if (indexed === undefined || docs === undefined || indexed.schema !== schema && schemaKeyOf(indexed.schema) !== schemaKeyOf(schema)) {
|
|
10179
|
+
removeModule(searchIndex, moduleFilePath);
|
|
10180
|
+
indexed = {
|
|
10181
|
+
schema: schema,
|
|
10182
|
+
entries: new Map()
|
|
10183
|
+
};
|
|
10184
|
+
docs = new Set();
|
|
10185
|
+
entriesByModule.set(moduleFilePath, indexed);
|
|
10186
|
+
docsByModule.set(moduleFilePath, docs);
|
|
10187
|
+
}
|
|
10188
|
+
var _indexed = indexed,
|
|
10189
|
+
entries = _indexed.entries;
|
|
10190
|
+
var _iterator = _createForOfIteratorHelper(entries),
|
|
10191
|
+
_step;
|
|
10192
|
+
try {
|
|
10193
|
+
for (_iterator.s(); !(_step = _iterator.n()).done;) {
|
|
10194
|
+
var _step$value = _slicedToArray(_step.value, 2),
|
|
10195
|
+
_key = _step$value[0],
|
|
10196
|
+
was = _step$value[1];
|
|
10197
|
+
if (Object.prototype.hasOwnProperty.call(source, _key) && source[_key] === was.source) {
|
|
10198
|
+
continue;
|
|
10199
|
+
}
|
|
10200
|
+
var _iterator2 = _createForOfIteratorHelper(was.docs),
|
|
10201
|
+
_step2;
|
|
10202
|
+
try {
|
|
10203
|
+
for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
|
|
10204
|
+
var _id = _step2.value;
|
|
10205
|
+
index.remove(_id);
|
|
10206
|
+
pathToLabel["delete"](_id);
|
|
10207
|
+
docs["delete"](_id);
|
|
10208
|
+
}
|
|
10209
|
+
} catch (err) {
|
|
10210
|
+
_iterator2.e(err);
|
|
10211
|
+
} finally {
|
|
10212
|
+
_iterator2.f();
|
|
8036
10213
|
}
|
|
10214
|
+
entries["delete"](_key);
|
|
8037
10215
|
}
|
|
10216
|
+
} catch (err) {
|
|
10217
|
+
_iterator.e(err);
|
|
10218
|
+
} finally {
|
|
10219
|
+
_iterator.f();
|
|
10220
|
+
}
|
|
10221
|
+
var path = moduleFilePath + "?p=";
|
|
10222
|
+
var walked = 0;
|
|
10223
|
+
var _loop = function _loop() {
|
|
10224
|
+
if (entries.has(key)) return 0; // continue
|
|
10225
|
+
if (until !== undefined && ++walked % ENTRIES_PER_CLOCK_CHECK === 0 && performance.now() > until) {
|
|
10226
|
+
return {
|
|
10227
|
+
v: false
|
|
10228
|
+
};
|
|
10229
|
+
}
|
|
10230
|
+
var value = source[key];
|
|
10231
|
+
var entryDocs = [];
|
|
10232
|
+
traverseSchemaSource(value, schema.item, sourcePathOfChild(path, key), function (leaf) {
|
|
10233
|
+
var id = indexLeaf(searchIndex, leaf);
|
|
10234
|
+
if (id === null) return;
|
|
10235
|
+
entryDocs.push(id);
|
|
10236
|
+
docs.add(id);
|
|
10237
|
+
});
|
|
10238
|
+
entries.set(key, {
|
|
10239
|
+
source: value,
|
|
10240
|
+
docs: entryDocs
|
|
10241
|
+
});
|
|
10242
|
+
},
|
|
10243
|
+
_ret;
|
|
10244
|
+
for (var key in source) {
|
|
10245
|
+
_ret = _loop();
|
|
10246
|
+
if (_ret === 0) continue;
|
|
10247
|
+
if (_ret) return _ret.v;
|
|
10248
|
+
}
|
|
10249
|
+
return true;
|
|
10250
|
+
}
|
|
10251
|
+
function isJsonRecord(source) {
|
|
10252
|
+
return _typeof(source) === "object" && source !== null && !Array.isArray(source);
|
|
10253
|
+
}
|
|
10254
|
+
|
|
10255
|
+
/**
|
|
10256
|
+
* A schema by its content, kept per schema object. Its object is not enough:
|
|
10257
|
+
* the Studio receives a module's schema again (new code, a re-read of the
|
|
10258
|
+
* project) as a new object that says the same, and starting a record of
|
|
10259
|
+
* 10,000 entries over for that was seconds of removing and adding
|
|
10260
|
+
* documents in one go.
|
|
10261
|
+
*/
|
|
10262
|
+
var schemaKeys = new WeakMap();
|
|
10263
|
+
function schemaKeyOf(schema) {
|
|
10264
|
+
var key = schemaKeys.get(schema);
|
|
10265
|
+
if (key === undefined) {
|
|
10266
|
+
key = JSON.stringify(schema);
|
|
10267
|
+
schemaKeys.set(schema, key);
|
|
10268
|
+
}
|
|
10269
|
+
return key;
|
|
10270
|
+
}
|
|
10271
|
+
|
|
10272
|
+
/** One leaf as a document, if it has anything to find. Its id, or `null`. */
|
|
10273
|
+
function indexLeaf(searchIndex, _ref) {
|
|
10274
|
+
var source = _ref.source,
|
|
10275
|
+
schema = _ref.schema,
|
|
10276
|
+
path = _ref.path;
|
|
10277
|
+
if (source === null) {
|
|
10278
|
+
return null;
|
|
10279
|
+
}
|
|
10280
|
+
var index = searchIndex.index,
|
|
10281
|
+
pathToLabel = searchIndex.pathToLabel;
|
|
10282
|
+
var searchText = "";
|
|
10283
|
+
var label = "";
|
|
8038
10284
|
|
|
8039
|
-
|
|
8040
|
-
|
|
8041
|
-
|
|
8042
|
-
|
|
8043
|
-
|
|
8044
|
-
|
|
8045
|
-
|
|
8046
|
-
|
|
8047
|
-
|
|
10285
|
+
// Handle primitives
|
|
10286
|
+
if (schema.type === "string" || schema.type === "number" || schema.type === "boolean" || schema.type === "date" || schema.type === "dateTime" || schema.type === "color" || schema.type === "code" || schema.type === "keyOf" || schema.type === "route" ||
|
|
10287
|
+
// An enum's value is a string the editor chose; it is as searchable as
|
|
10288
|
+
// any other string leaf. The old string union was never indexed at all,
|
|
10289
|
+
// which is the gap this closes.
|
|
10290
|
+
schema.type === "enum" ||
|
|
10291
|
+
// A locale tag, for the same reason: it is what the editor picked and
|
|
10292
|
+
// what they see in the field, so searching "nb-NO" has to find the
|
|
10293
|
+
// Norwegian rows. `traverseSchemaSource` visits these, and visiting a
|
|
10294
|
+
// leaf only to index it as the empty string is worse than not visiting
|
|
10295
|
+
// it — it looks like content that does not exist.
|
|
10296
|
+
schema.type === "locale") {
|
|
10297
|
+
var _source$toString, _source$toString2;
|
|
10298
|
+
searchText = (_source$toString = source === null || source === void 0 ? void 0 : source.toString()) !== null && _source$toString !== void 0 ? _source$toString : "";
|
|
10299
|
+
label = (_source$toString2 = source === null || source === void 0 ? void 0 : source.toString()) !== null && _source$toString2 !== void 0 ? _source$toString2 : "";
|
|
10300
|
+
} else if (schema.type === "literal") {
|
|
10301
|
+
searchText = schema.value.toString();
|
|
10302
|
+
label = schema.value.toString();
|
|
10303
|
+
}
|
|
10304
|
+
// Handle richtext - flatten to get text content
|
|
10305
|
+
else if (schema.type === "richtext") {
|
|
10306
|
+
searchText = flattenRichText(source);
|
|
10307
|
+
// Use first 50 chars as label
|
|
10308
|
+
label = searchText.substring(0, 50) || "richtext";
|
|
10309
|
+
}
|
|
10310
|
+
// Handle file/image/video
|
|
10311
|
+
else if (schema.type === "file" || schema.type === "image" || schema.type === "video") {
|
|
10312
|
+
if (source !== null && _typeof(source) === "object" && "path" in source && typeof source.path === "string") {
|
|
10313
|
+
// The label is the bare filename - the folder is shown separately in
|
|
10314
|
+
// the UI - but both are searchable.
|
|
10315
|
+
var _getRefParts = getRefParts(source.path),
|
|
10316
|
+
filename = _getRefParts.filename,
|
|
10317
|
+
folder = _getRefParts.folder;
|
|
10318
|
+
var alt = "alt" in source ? source.alt : "";
|
|
10319
|
+
searchText = filename + " " + folder + " " + alt;
|
|
10320
|
+
label = filename;
|
|
8048
10321
|
}
|
|
8049
|
-
}
|
|
10322
|
+
}
|
|
10323
|
+
|
|
10324
|
+
// Add to index if we have search text
|
|
10325
|
+
if (!searchText) {
|
|
10326
|
+
return null;
|
|
10327
|
+
}
|
|
10328
|
+
var _Internal$splitModule = Internal.splitModuleFilePathAndModulePath(path),
|
|
10329
|
+
_Internal$splitModule2 = _slicedToArray(_Internal$splitModule, 2),
|
|
10330
|
+
modulePath = _Internal$splitModule2[1];
|
|
10331
|
+
var cleanPath = fastRemoveNonWordChars(modulePath) + " ";
|
|
10332
|
+
index.add(path, cleanPath + " " + path + " " + searchText);
|
|
10333
|
+
pathToLabel.set(path, label);
|
|
10334
|
+
return path;
|
|
8050
10335
|
}
|
|
8051
10336
|
|
|
8052
10337
|
/**
|
|
@@ -8058,21 +10343,24 @@ function indexModule(searchIndex, moduleFilePath, source, schema) {
|
|
|
8058
10343
|
function removeModule(searchIndex, moduleFilePath) {
|
|
8059
10344
|
var index = searchIndex.index,
|
|
8060
10345
|
pathToLabel = searchIndex.pathToLabel,
|
|
8061
|
-
docsByModule = searchIndex.docsByModule
|
|
10346
|
+
docsByModule = searchIndex.docsByModule,
|
|
10347
|
+
entriesByModule = searchIndex.entriesByModule;
|
|
10348
|
+
entriesByModule["delete"](moduleFilePath);
|
|
10349
|
+
searchIndex.walkedByModule["delete"](moduleFilePath);
|
|
8062
10350
|
var existing = docsByModule.get(moduleFilePath);
|
|
8063
10351
|
if (existing === undefined) return;
|
|
8064
|
-
var
|
|
8065
|
-
|
|
10352
|
+
var _iterator3 = _createForOfIteratorHelper(existing),
|
|
10353
|
+
_step3;
|
|
8066
10354
|
try {
|
|
8067
|
-
for (
|
|
8068
|
-
var
|
|
8069
|
-
index.remove(
|
|
8070
|
-
pathToLabel["delete"](
|
|
10355
|
+
for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
|
|
10356
|
+
var _id2 = _step3.value;
|
|
10357
|
+
index.remove(_id2);
|
|
10358
|
+
pathToLabel["delete"](_id2);
|
|
8071
10359
|
}
|
|
8072
10360
|
} catch (err) {
|
|
8073
|
-
|
|
10361
|
+
_iterator3.e(err);
|
|
8074
10362
|
} finally {
|
|
8075
|
-
|
|
10363
|
+
_iterator3.f();
|
|
8076
10364
|
}
|
|
8077
10365
|
docsByModule["delete"](moduleFilePath);
|
|
8078
10366
|
}
|
|
@@ -9547,4 +11835,4 @@ var messageOf = function messageOf(error) {
|
|
|
9547
11835
|
return error instanceof Error ? error.message : String(error);
|
|
9548
11836
|
};
|
|
9549
11837
|
|
|
9550
|
-
export { AA_LARGE, AA_TEXT, AIToolCallSummaries, AnalyticsBeacon, Api, BRAND_CONTRAST_PAIRS, BinaryFileRef, CodeOptions, ColorOptions, DEFAULT_ERROR_MESSAGE, DateOptions, ENCODE_DEFAULTS, ENCODE_MIME_TYPE_OF, FileOptions, HistoricalCommit, HistoricalModule, HistoricalPatchSet, HistoryError, ImageEncodeOption, ImageOptions, JSONValue, JSON_ENTRIES_BATCH_MAX, LOCAL_FILE_IN_REMOTE_PROJECT_HINT, LOCAL_FILE_IN_REMOTE_PROJECT_WARNING, ModuleComparison, NEVER_ENCODED, ParentRef$1 as ParentRef, Patch, PatchBlock, PatchId, PatchJSON, PatchSets, PlanRefusalError, RAMP_STEPS, RemirrorBr, RemirrorBulletList, RemirrorHeading, RemirrorImage, RemirrorJSON, RemirrorLinkMark, RemirrorListItem, RemirrorOrderedList, RemirrorParagraph, RemirrorText, RemirrorTextMark, RestoreVerdict, RichTextOptions, SCHEMA_SOURCE_FIXES, SELF_CHECK_VERSION, SKETCHPAD_MAX_TEXT_LENGTH, STUDIO_FIXES, SerializedArraySchema, SerializedBooleanSchema, SerializedCodeSchema, SerializedColorSchema, SerializedDateSchema, SerializedDateTimeSchema, SerializedDiscriminatedUnionSchema, SerializedEnumSchema, SerializedFileSchema, SerializedImageSchema, SerializedKeyOfSchema, SerializedLiteralSchema, SerializedLocaleSchema, SerializedNumberSchema, SerializedObjectSchema, SerializedRecordSchema, SerializedRichTextSchema, SerializedRouteSchema, SerializedSchema, SerializedSettingsSchema, SerializedStringSchema, SerializedValViewSchema, SerializedVideoSchema, SharedValConfig, Sketchpad, SketchpadSummary, VAL_ENABLE_COOKIE_NAME, VAL_GREEN_RAMP, VAL_SESSION_COOKIE, VAL_STATE_COOKIE, VAL_STUDIO_HEADER, ValCommit, ValDeployment, VideoOptions, VideoStreamOption, WARNING_FIXES, WASM_ENCODED_MIME_TYPES, accentRamp, buildDuplicatePatch, buildEmptyAtPathPatch, buildFileRefValue, buildImageGalleryPatch, buildRemoveImageGalleryEntryPatch, buildSearchIndex, chainHeadOf, checkCompatibility, checkSketch, childSource, chooseEncoded, concatModulePath, contrastRatio, createRegExpFromPattern, createSearchIndex, createValClient, declaredLocales, describeContainerAtPath, emptyOf, enumLocaleTag, explainIncompatible, fallbackMimeType, filterBlockingValidationErrors, filterRoutesByPatterns, findSimilar, fitWithin, flattenRichText, forEachRichTextImage, formatSketch, formatSketchBox, formatSketchText, getErrorMessageFromUnknownJson, getFilenameFromRef, getInlineImgInfo, getKeyOfRecordAt, getRefParts, getRoutesOf, getRoutesWithModulePaths, getSourceAt, getValDemo, hasLocaleRouteParam, indexModule, isInsidePatchSetPath, isJsonObject, isLocaleSchema, isMimeTypeAccepted, isRemoteSchema, isRepairedOnSave, isSchemaSourceFixError, isSkippedSource, isStudioFix, isValidationWarning, isWarningFix, joinSharedChanges, keyOfRecordPath, levenshtein, localeAt, localeMeantBy, localeOfModuleRoot, localeOfRouteKey, localeParamOfRouteKey, localeScopeAt,
|
|
11838
|
+
export { AA_LARGE, AA_TEXT, AIToolCallSummaries, AnalyticsBeacon, Api, BRAND_CONTRAST_PAIRS, BinaryFileRef, ChainFold, CodeOptions, ColorOptions, DEFAULT_ERROR_MESSAGE, DateOptions, EMPTY_CHAIN, ENCODE_DEFAULTS, ENCODE_MIME_TYPE_OF, ENGINE, FileOptions, HistoricalCommit, HistoricalModule, HistoricalPatchSet, HistoryError, ImageEncodeOption, ImageOptions, JSONValue, JSON_ENTRIES_BATCH_MAX, LOCAL_FILE_IN_REMOTE_PROJECT_HINT, LOCAL_FILE_IN_REMOTE_PROJECT_WARNING, ModuleComparison, ModuleVersionSchema, NEVER_ENCODED, ParentRef$1 as ParentRef, Patch, PatchBlock, PatchId, PatchJSON, PatchSets, PlanRefusalError, ProjectVersionSchema, RAMP_STEPS, RemirrorBr, RemirrorBulletList, RemirrorHeading, RemirrorImage, RemirrorJSON, RemirrorLinkMark, RemirrorListItem, RemirrorOrderedList, RemirrorParagraph, RemirrorText, RemirrorTextMark, RestoreVerdict, RichTextOptions, SCHEMA_SOURCE_FIXES, SELF_CHECK_VERSION, SESSION_MEDIA_PREFIX, SKETCHPAD_MAX_TEXT_LENGTH, STAGED_SCHEMA_PREFIX, STUDIO_FIXES, SerializedArraySchema, SerializedBooleanSchema, SerializedCodeSchema, SerializedColorSchema, SerializedDateSchema, SerializedDateTimeSchema, SerializedDiscriminatedUnionSchema, SerializedEnumSchema, SerializedFileSchema, SerializedImageSchema, SerializedKeyOfSchema, SerializedLiteralSchema, SerializedLocaleSchema, SerializedNumberSchema, SerializedObjectSchema, SerializedRecordSchema, SerializedRichTextSchema, SerializedRouteSchema, SerializedSchema, SerializedSettingsSchema, SerializedStringSchema, SerializedValViewSchema, SerializedVideoSchema, SharedValConfig, Sketchpad, SketchpadSummary, VAL_CLIENT_HEADER, VAL_ENABLE_COOKIE_NAME, VAL_GREEN_RAMP, VAL_SESSION_COOKIE, VAL_STATE_COOKIE, VAL_STUDIO_HEADER, ValCommit, ValDeployment, VideoOptions, VideoStreamOption, WARNING_FIXES, WASM_ENCODED_MIME_TYPES, accentRamp, applyChain, applyPatchRecord, baseModuleContent, baseVersionOf, buildDuplicatePatch, buildEmptyAtPathPatch, buildFileRefValue, buildImageGalleryPatch, buildRemoveImageGalleryEntryPatch, buildSearchIndex, bundleTier, chainHeadOf, chainOf, checkCompatibility, checkSketch, childSource, chooseEncoded, classifyJsonValuesOp, concatModulePath, contrastRatio, createRegExpFromPattern, createSearchIndex, createValClient, declaredLocales, describeContainerAtPath, emptyOf, engineWork, entryAddress, entryState, enumLocaleTag, expandJsonValuesRootOp, expandJsonValuesRootOpForKey, explainIncompatible, extendChain, fallbackMimeType, filterBlockingValidationErrors, filterRoutesByPatterns, findSimilar, fitWithin, flattenRichText, forEachRichTextImage, formatSketch, formatSketchBox, formatSketchText, getErrorMessageFromUnknownJson, getFilenameFromRef, getInlineImgInfo, getKeyOfRecordAt, getRefParts, getRoutesOf, getRoutesWithModulePaths, getSourceAt, getValDemo, hasLocaleRouteParam, indexModule, isInsidePatchSetPath, isJsonObject, isJsonValuesRootOp, isLocaleSchema, isMimeTypeAccepted, isRemoteSchema, isRepairedOnSave, isSchemaSourceFixError, isSkippedSource, isStudioFix, isValidationWarning, isWarningFix, joinSharedChanges, keyOfRecordPath, levenshtein, localeAt, localeMeantBy, localeOfModuleRoot, localeOfRouteKey, localeParamOfRouteKey, localeScopeAt, memoryTier, moduleKey, newestCommitSha, parseModuleKey, parseSketch, partitionValidationErrors, pathOfModuleKey, performSearch, proposalCustomProperties, rebaseContentOp, relativeLuminance, relocateForLocale, remirrorToRichTextSource, removeModule, resolveEncodeSettings, resolveEntry, resolvePairGround, resolveSchemaSourceFixForError, resolveSchemaSourceFixes, resolveSerializedSchemaAtPath, richTextImageSchema, richTextToRemirror, safeParsePatch, sameEntryVersion, sameModuleVersion, schemaErrorsOf, schemaTypesOfPath, schemaVersionOf, selectionClosure, selfCheck, shouldEncodeWithWasm, sideFiles, sideSource, sketchFieldTexts, sketchRoutes, sketchTitle, sourcePathOfChild, storeWork, themeCustomProperties, tiered, toolNames, traverseSchemaSource, urlOf, valClientHeaders, valModulesEntryFile, valModulesEntryPath, validateRoutePatterns, versionHashCount, wholeEntryKey, withExtension };
|