@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
|
@@ -5,11 +5,13 @@ Object.defineProperty(exports, '__esModule', { value: true });
|
|
|
5
5
|
var createForOfIteratorHelper = require('../../dist/createForOfIteratorHelper-f2d7ac6d.cjs.dev.js');
|
|
6
6
|
var core = require('@valbuild/core');
|
|
7
7
|
var zod = require('zod');
|
|
8
|
-
var
|
|
8
|
+
var projectSource = require('../../dist/projectSource-2a58ef15.cjs.dev.js');
|
|
9
|
+
var wrapNativeSuper = require('../../dist/wrapNativeSuper-933f991e.cjs.dev.js');
|
|
9
10
|
var zodValidationError = require('zod-validation-error');
|
|
10
|
-
var
|
|
11
|
+
var fp = require('@valbuild/core/fp');
|
|
12
|
+
var patch = require('@valbuild/core/patch');
|
|
13
|
+
var getSitemapTree = require('../../dist/getSitemapTree-59c5ed5d.cjs.dev.js');
|
|
11
14
|
var FlexSearch = require('flexsearch');
|
|
12
|
-
var wrapNativeSuper = require('../../dist/wrapNativeSuper-933f991e.cjs.dev.js');
|
|
13
15
|
var utils = require('../../dist/utils-d3d8b4b5.cjs.dev.js');
|
|
14
16
|
|
|
15
17
|
function _interopDefault (e) { return e && e.__esModule ? e : { 'default': e }; }
|
|
@@ -204,7 +206,7 @@ var convertToNumber = function convertToNumber(value) {
|
|
|
204
206
|
var parsedValue = parseInt(value, 10);
|
|
205
207
|
return isNaN(parsedValue) ? 0 : parsedValue;
|
|
206
208
|
};
|
|
207
|
-
var textEncoder = new TextEncoder();
|
|
209
|
+
var textEncoder$1 = new TextEncoder();
|
|
208
210
|
function convertImageNode(path, node, files, configDirectory, remoteOptions) {
|
|
209
211
|
if (node.attrs && node.attrs.src.startsWith("data:")) {
|
|
210
212
|
var binaryData = utils.base64DataUrlToUint8Array(node.attrs.src);
|
|
@@ -230,7 +232,7 @@ function convertImageNode(path, node, files, configDirectory, remoteOptions) {
|
|
|
230
232
|
validationHash: core.Internal.remote.getValidationHash(remoteOptions.coreVersion, remoteOptions.schema, mimeType, {
|
|
231
233
|
width: typeof node.attrs.width === "number" ? node.attrs.width : 0,
|
|
232
234
|
height: typeof node.attrs.height === "number" ? node.attrs.height : 0
|
|
233
|
-
}, remoteFileHash, textEncoder),
|
|
235
|
+
}, remoteFileHash, textEncoder$1),
|
|
234
236
|
filePath: filePath.slice(1)
|
|
235
237
|
})) : filePath;
|
|
236
238
|
if (existingFilesEntry) {
|
|
@@ -748,62 +750,9 @@ var VAL_STATE_COOKIE = core.Internal.VAL_STATE_COOKIE;
|
|
|
748
750
|
var VAL_ENABLE_COOKIE_NAME = core.Internal.VAL_ENABLE_COOKIE_NAME;
|
|
749
751
|
|
|
750
752
|
/**
|
|
751
|
-
*
|
|
752
|
-
*
|
|
753
|
-
* `fetchVal` in draft mode reads the whole module tree out of `/sources/~`, and
|
|
754
|
-
* a page that calls it three times used to ask three times — the answer being
|
|
755
|
-
* byte-identical each time, because the query and the session are fixed for the
|
|
756
|
-
* length of a request. This is what makes the second and third call free.
|
|
757
|
-
*
|
|
758
|
-
* ## Why the scope is the REQUEST
|
|
759
|
-
*
|
|
760
|
-
* The memoised response is a DRAFT -- unpublished work, read with the caller's
|
|
761
|
-
* session cookie. Every caller allowed to read drafts gets the same draft (patch
|
|
762
|
-
* groups, which made it per author, are gone), but whether a caller is allowed
|
|
763
|
-
* at all is decided per session. A memo that outlived the request would hand
|
|
764
|
-
* the draft to the next visitor without asking, and it would be invisible: the
|
|
765
|
-
* page would simply render somebody's half-finished sentence. Sharing it across
|
|
766
|
-
* requests is possible now, keyed on the chain's head, and would be its own
|
|
767
|
-
* measured change.
|
|
768
|
-
*
|
|
769
|
-
* So this module holds NO state of its own. The caller supplies the box, and
|
|
770
|
-
* the box has to come from something the framework already scopes to the
|
|
771
|
-
* request — React's `cache()` in an RSC, a `WeakMap` keyed on the `Request` in
|
|
772
|
-
* TanStack Start. `null` means "no request scope available", and that is not an
|
|
773
|
-
* error: it falls back to computing every time, which is what the code did
|
|
774
|
-
* before this existed. Ineffective is a safe failure here; leaking is not.
|
|
775
|
-
*
|
|
776
|
-
* The `key` is the second half of that guarantee. It carries whatever the
|
|
777
|
-
* answer depends on — the session — so a box that somehow outlived its request,
|
|
778
|
-
* or is shared by two sessions, MISSES rather than serving the wrong draft.
|
|
779
|
-
*
|
|
780
|
-
* ## The promise is cached, not the value
|
|
781
|
-
*
|
|
782
|
-
* React renders siblings concurrently, so three `fetchVal` calls in one page
|
|
783
|
-
* are usually in flight at the same time rather than one after another.
|
|
784
|
-
* Caching the resolved value would dedupe none of them; caching the promise
|
|
785
|
-
* dedupes both orderings.
|
|
786
|
-
*/
|
|
787
|
-
|
|
788
|
-
/**
|
|
789
|
-
* Returns the memoised promise for `key`, computing it only on a miss.
|
|
790
|
-
*
|
|
791
|
-
* A rejection is memoised along with everything else, on purpose: one failed
|
|
792
|
-
* read per request is the same answer for every caller in it, and retrying it
|
|
793
|
-
* two more times only makes a broken draft slower to render.
|
|
753
|
+
* Nothing moved since the ETag the caller sent back: no body, as HTTP
|
|
754
|
+
* requires of a 304.
|
|
794
755
|
*/
|
|
795
|
-
function memoizePerRequest(box, key, compute) {
|
|
796
|
-
if (box === null) {
|
|
797
|
-
return compute();
|
|
798
|
-
}
|
|
799
|
-
if (box.promise !== undefined && box.key === key) {
|
|
800
|
-
return box.promise;
|
|
801
|
-
}
|
|
802
|
-
var promise = compute();
|
|
803
|
-
box.key = key;
|
|
804
|
-
box.promise = promise;
|
|
805
|
-
return promise;
|
|
806
|
-
}
|
|
807
756
|
|
|
808
757
|
/**
|
|
809
758
|
* JSON, recursively — the shape of a Source over the wire.
|
|
@@ -1054,7 +1003,7 @@ var readLegacyDirectoryAsDir = function readLegacyDirectoryAsDir(value) {
|
|
|
1054
1003
|
}
|
|
1055
1004
|
var _ref = value,
|
|
1056
1005
|
directory = _ref.directory,
|
|
1057
|
-
rest =
|
|
1006
|
+
rest = projectSource._objectWithoutProperties(_ref, _excluded);
|
|
1058
1007
|
return createForOfIteratorHelper._objectSpread2(createForOfIteratorHelper._objectSpread2({}, rest), {}, {
|
|
1059
1008
|
dir: directory
|
|
1060
1009
|
});
|
|
@@ -1309,6 +1258,228 @@ var ValDeployment = zod.z.object({
|
|
|
1309
1258
|
commitMessage: zod.z.string().nullable().optional()
|
|
1310
1259
|
});
|
|
1311
1260
|
|
|
1261
|
+
var _Internal$VERSION$cor;
|
|
1262
|
+
|
|
1263
|
+
/**
|
|
1264
|
+
* What a module's content IS: the schema it is read and patched with, the
|
|
1265
|
+
* base it starts from, and the chain of patches on top. Three parts because
|
|
1266
|
+
* each kind of change moves exactly one of them (see
|
|
1267
|
+
* `docs/plans/one-content-model.md`, "A version, per module, with the code in
|
|
1268
|
+
* it"):
|
|
1269
|
+
*
|
|
1270
|
+
* - a schema change, or a new engine (`@valbuild/*` upgraded), moves `schema`;
|
|
1271
|
+
* - a publish, a content version, a literal edited in code, moves `base`;
|
|
1272
|
+
* - an edit, a discard, a publish (which empties the chain) moves `chain`.
|
|
1273
|
+
*
|
|
1274
|
+
* Two answers with the same version are the same content, wherever they came
|
|
1275
|
+
* from. That is the whole point: comparing contents becomes comparing three
|
|
1276
|
+
* strings.
|
|
1277
|
+
*
|
|
1278
|
+
* Computed when content CHANGES -- a module evaluated, a base adopted, a patch
|
|
1279
|
+
* appended -- never when it is read (risk 3 in the plan): every function here
|
|
1280
|
+
* that hashes counts itself ({@link versionHashCount}), so a reader that
|
|
1281
|
+
* hashes is caught by a counted budget rather than by a profile.
|
|
1282
|
+
*/
|
|
1283
|
+
|
|
1284
|
+
/**
|
|
1285
|
+
* What a project answer is relative to: which build served it, and how far
|
|
1286
|
+
* along the project's changes it is.
|
|
1287
|
+
*/
|
|
1288
|
+
|
|
1289
|
+
/**
|
|
1290
|
+
* What the schema version of a module staged after the build serving a
|
|
1291
|
+
* proposal starts with: `staged:<inputs>`. Val's server has not got that
|
|
1292
|
+
* module's schema, so it names the staging instead; a Studio compares no
|
|
1293
|
+
* build's code against it.
|
|
1294
|
+
*/
|
|
1295
|
+
var STAGED_SCHEMA_PREFIX = "staged:";
|
|
1296
|
+
|
|
1297
|
+
/** The chain of no patches. */
|
|
1298
|
+
var EMPTY_CHAIN = "";
|
|
1299
|
+
|
|
1300
|
+
/**
|
|
1301
|
+
* The engine: how patches are applied and content is read. Part of every
|
|
1302
|
+
* schema version, because content read by another engine is not the same
|
|
1303
|
+
* content even under the same schema.
|
|
1304
|
+
*/
|
|
1305
|
+
var ENGINE = "@valbuild/core@".concat((_Internal$VERSION$cor = core.Internal.VERSION.core) !== null && _Internal$VERSION$cor !== void 0 ? _Internal$VERSION$cor : "unknown");
|
|
1306
|
+
var hashes = 0;
|
|
1307
|
+
var textEncoder;
|
|
1308
|
+
function sha256Hex(input) {
|
|
1309
|
+
hashes++;
|
|
1310
|
+
textEncoder !== null && textEncoder !== void 0 ? textEncoder : textEncoder = new TextEncoder();
|
|
1311
|
+
return core.Internal.getSHA256Hash(textEncoder.encode(input));
|
|
1312
|
+
}
|
|
1313
|
+
|
|
1314
|
+
/**
|
|
1315
|
+
* How many times a version has been hashed in this process: the counted
|
|
1316
|
+
* budget behind "versions are computed when content changes, never when it
|
|
1317
|
+
* is read" (`version:hash`). A test takes it before and after a read.
|
|
1318
|
+
*/
|
|
1319
|
+
function versionHashCount() {
|
|
1320
|
+
return hashes;
|
|
1321
|
+
}
|
|
1322
|
+
|
|
1323
|
+
/** A chain one patch longer. */
|
|
1324
|
+
function extendChain(prev, id) {
|
|
1325
|
+
return sha256Hex("".concat(prev, "\n").concat(id));
|
|
1326
|
+
}
|
|
1327
|
+
|
|
1328
|
+
/** The chain of these patch ids, in order, on top of a base. */
|
|
1329
|
+
function chainOf(ids) {
|
|
1330
|
+
var chain = EMPTY_CHAIN;
|
|
1331
|
+
var _iterator = createForOfIteratorHelper._createForOfIteratorHelper(ids),
|
|
1332
|
+
_step;
|
|
1333
|
+
try {
|
|
1334
|
+
for (_iterator.s(); !(_step = _iterator.n()).done;) {
|
|
1335
|
+
var id = _step.value;
|
|
1336
|
+
chain = extendChain(chain, id);
|
|
1337
|
+
}
|
|
1338
|
+
} catch (err) {
|
|
1339
|
+
_iterator.e(err);
|
|
1340
|
+
} finally {
|
|
1341
|
+
_iterator.f();
|
|
1342
|
+
}
|
|
1343
|
+
return chain;
|
|
1344
|
+
}
|
|
1345
|
+
|
|
1346
|
+
/**
|
|
1347
|
+
* A module's schema version, from its serialized schema. The same function
|
|
1348
|
+
* on the server (from what `extractValModules` serialized) and in the Studio
|
|
1349
|
+
* (from what it holds in `SchemaStore`), so the two compare.
|
|
1350
|
+
*/
|
|
1351
|
+
function schemaVersionOf(serializedSchema) {
|
|
1352
|
+
return sha256Hex("".concat(ENGINE, "\n").concat(JSON.stringify(serializedSchema)));
|
|
1353
|
+
}
|
|
1354
|
+
|
|
1355
|
+
/**
|
|
1356
|
+
* A module's base version, from its base source: what the module evaluates
|
|
1357
|
+
* to, or what a content version or a publish put in its place. `.jsonValues()`
|
|
1358
|
+
* entries are markers here; an entry's own version comes with entries stored
|
|
1359
|
+
* as modules of their own (phase 6).
|
|
1360
|
+
*/
|
|
1361
|
+
function baseVersionOf(source) {
|
|
1362
|
+
var _JSON$stringify;
|
|
1363
|
+
return sha256Hex((_JSON$stringify = JSON.stringify(source)) !== null && _JSON$stringify !== void 0 ? _JSON$stringify : "undefined");
|
|
1364
|
+
}
|
|
1365
|
+
|
|
1366
|
+
/** The address of a module's content. Never changes meaning. */
|
|
1367
|
+
function moduleKey(path, v) {
|
|
1368
|
+
return "".concat(path, "@").concat(v.schema, ".").concat(v.base, ".").concat(v.chain);
|
|
1369
|
+
}
|
|
1370
|
+
|
|
1371
|
+
/** Whether two versions name the same content. */
|
|
1372
|
+
function sameModuleVersion(a, b) {
|
|
1373
|
+
return a.schema === b.schema && a.base === b.base && a.chain === b.chain;
|
|
1374
|
+
}
|
|
1375
|
+
|
|
1376
|
+
/**
|
|
1377
|
+
* The chains of every module, from the patches a draft applies (in chain
|
|
1378
|
+
* order), folded incrementally.
|
|
1379
|
+
*
|
|
1380
|
+
* A read asks for the chains every time; the fold is extended only by the
|
|
1381
|
+
* patches the chain has gained since the last call, and a call that finds the
|
|
1382
|
+
* chain as it was hashes nothing. A chain that changed anywhere but at its end
|
|
1383
|
+
* (a discard in the middle, a publish) is folded again from the start --
|
|
1384
|
+
* once, when that is first noticed.
|
|
1385
|
+
*/
|
|
1386
|
+
var ChainFold = /*#__PURE__*/function () {
|
|
1387
|
+
function ChainFold() {
|
|
1388
|
+
wrapNativeSuper._classCallCheck(this, ChainFold);
|
|
1389
|
+
createForOfIteratorHelper._defineProperty(this, "ids", []);
|
|
1390
|
+
createForOfIteratorHelper._defineProperty(this, "paths", []);
|
|
1391
|
+
createForOfIteratorHelper._defineProperty(this, "chains", new Map());
|
|
1392
|
+
/**
|
|
1393
|
+
* Every module's chain after each of its patches, in order: what
|
|
1394
|
+
* {@link chainAt} answers for a module whose content stopped part way
|
|
1395
|
+
* along its chain (a patch that does not apply). Kept as the chains are
|
|
1396
|
+
* extended, so asking hashes nothing.
|
|
1397
|
+
*/
|
|
1398
|
+
createForOfIteratorHelper._defineProperty(this, "prefixes", new Map());
|
|
1399
|
+
/**
|
|
1400
|
+
* Bumped whenever the chains move: a cheap identity for "the same chains as
|
|
1401
|
+
* before", in-process. With {@link instance} in front, two folds never name
|
|
1402
|
+
* one epoch: two processes (or isolates) holding the same chains answer
|
|
1403
|
+
* differently, which only costs a cache a miss, where two holding
|
|
1404
|
+
* DIFFERENT chains at the same count must never answer alike (an ETag
|
|
1405
|
+
* answered 304 for content the caller does not have).
|
|
1406
|
+
*/
|
|
1407
|
+
createForOfIteratorHelper._defineProperty(this, "epochValue", 0);
|
|
1408
|
+
createForOfIteratorHelper._defineProperty(this, "instance", Math.random().toString(36).slice(2, 10));
|
|
1409
|
+
}
|
|
1410
|
+
return wrapNativeSuper._createClass(ChainFold, [{
|
|
1411
|
+
key: "chainsOf",
|
|
1412
|
+
value: /** Every module's chain, for these patches. Modules with none are absent. */
|
|
1413
|
+
function chainsOf(patches) {
|
|
1414
|
+
var known = this.ids.length;
|
|
1415
|
+
var extends_ = patches.length >= known;
|
|
1416
|
+
for (var i = 0; extends_ && i < known; i++) {
|
|
1417
|
+
var patch = patches[i];
|
|
1418
|
+
extends_ = patch !== undefined && patch.patchId === this.ids[i] && patch.path === this.paths[i];
|
|
1419
|
+
}
|
|
1420
|
+
if (extends_ && patches.length === known) {
|
|
1421
|
+
return this.chains;
|
|
1422
|
+
}
|
|
1423
|
+
var chains = extends_ ? new Map(this.chains) : new Map();
|
|
1424
|
+
var from = extends_ ? known : 0;
|
|
1425
|
+
if (!extends_) {
|
|
1426
|
+
this.ids = [];
|
|
1427
|
+
this.paths = [];
|
|
1428
|
+
this.prefixes = new Map();
|
|
1429
|
+
}
|
|
1430
|
+
for (var _i = from; _i < patches.length; _i++) {
|
|
1431
|
+
var _chains$get;
|
|
1432
|
+
var _patch = patches[_i];
|
|
1433
|
+
if (_patch === undefined) continue;
|
|
1434
|
+
var chain = extendChain((_chains$get = chains.get(_patch.path)) !== null && _chains$get !== void 0 ? _chains$get : EMPTY_CHAIN, _patch.patchId);
|
|
1435
|
+
chains.set(_patch.path, chain);
|
|
1436
|
+
var prefixes = this.prefixes.get(_patch.path);
|
|
1437
|
+
if (prefixes === undefined) this.prefixes.set(_patch.path, [chain]);else prefixes.push(chain);
|
|
1438
|
+
this.ids.push(_patch.patchId);
|
|
1439
|
+
this.paths.push(_patch.path);
|
|
1440
|
+
}
|
|
1441
|
+
this.chains = chains;
|
|
1442
|
+
this.epochValue++;
|
|
1443
|
+
return chains;
|
|
1444
|
+
}
|
|
1445
|
+
|
|
1446
|
+
/**
|
|
1447
|
+
* A module's chain after its first `count` patches, of those the last
|
|
1448
|
+
* {@link chainsOf} was given: the version of content that stopped at a
|
|
1449
|
+
* patch that does not apply, so the version names the chain the content
|
|
1450
|
+
* is AT. Hashes nothing.
|
|
1451
|
+
*/
|
|
1452
|
+
}, {
|
|
1453
|
+
key: "chainAt",
|
|
1454
|
+
value: function chainAt(path, count) {
|
|
1455
|
+
var _this$prefixes$get, _ref, _prefixes;
|
|
1456
|
+
if (count <= 0) return EMPTY_CHAIN;
|
|
1457
|
+
var prefixes = (_this$prefixes$get = this.prefixes.get(path)) !== null && _this$prefixes$get !== void 0 ? _this$prefixes$get : [];
|
|
1458
|
+
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;
|
|
1459
|
+
}
|
|
1460
|
+
|
|
1461
|
+
/** See {@link epochValue}. */
|
|
1462
|
+
}, {
|
|
1463
|
+
key: "epoch",
|
|
1464
|
+
value: function epoch() {
|
|
1465
|
+
return "".concat(this.instance, "-").concat(this.epochValue);
|
|
1466
|
+
}
|
|
1467
|
+
}]);
|
|
1468
|
+
}();
|
|
1469
|
+
|
|
1470
|
+
/** {@link ModuleVersion} on the wire. */
|
|
1471
|
+
var ModuleVersionSchema = zod.z.object({
|
|
1472
|
+
schema: zod.z.string(),
|
|
1473
|
+
base: zod.z.string(),
|
|
1474
|
+
chain: zod.z.string()
|
|
1475
|
+
});
|
|
1476
|
+
|
|
1477
|
+
/** {@link ProjectVersion} on the wire. */
|
|
1478
|
+
var ProjectVersionSchema = zod.z.object({
|
|
1479
|
+
build: zod.z.string(),
|
|
1480
|
+
seq: zod.z.number()
|
|
1481
|
+
});
|
|
1482
|
+
|
|
1312
1483
|
/**
|
|
1313
1484
|
* The wire shapes for reading history.
|
|
1314
1485
|
*
|
|
@@ -1430,6 +1601,10 @@ var ModuleComparison = zod.z.object({
|
|
|
1430
1601
|
verdict: RestoreVerdict
|
|
1431
1602
|
});
|
|
1432
1603
|
|
|
1604
|
+
/** An `EntryVersion` on the wire. */
|
|
1605
|
+
var EntryVersionSchema = ModuleVersionSchema.extend({
|
|
1606
|
+
key: zod.z.string()
|
|
1607
|
+
});
|
|
1433
1608
|
var ModuleFilePath = zod.z.string().refine(function (_path) {
|
|
1434
1609
|
return true;
|
|
1435
1610
|
} // TODO: validation
|
|
@@ -2052,11 +2227,112 @@ var Api = {
|
|
|
2052
2227
|
})])
|
|
2053
2228
|
}
|
|
2054
2229
|
},
|
|
2230
|
+
/**
|
|
2231
|
+
* What each module IS, right now: its version (`schema`, `base`, `chain`),
|
|
2232
|
+
* and the project version that is relative to. What `/stat` answers, per
|
|
2233
|
+
* module; see `docs/plans/one-content-model.md`, "The Studio loads
|
|
2234
|
+
* everything from one place".
|
|
2235
|
+
*
|
|
2236
|
+
* It has its own ETag, so a client that sends it back (`If-None-Match`)
|
|
2237
|
+
* gets a 304 and no body while nothing has moved. Computed from versions
|
|
2238
|
+
* kept as content changes: answering it hashes nothing.
|
|
2239
|
+
*
|
|
2240
|
+
* "Since `seq` N" (`?since=N`, with the ETag of the manifest the client
|
|
2241
|
+
* holds, which was answered at `seq` N, as `If-None-Match`): the modules
|
|
2242
|
+
* whose version moved since that manifest, and no others, with `since: N`
|
|
2243
|
+
* on the answer to say it is a delta. The ETag is in the body too (`tag`),
|
|
2244
|
+
* for a client that reads answers through `ValClient`, which hands over
|
|
2245
|
+
* the body and not the headers. Answered whole -- no `since` -- when the server no
|
|
2246
|
+
* longer holds the manifest that ETag names (another process, another
|
|
2247
|
+
* build, too long ago), and by a server of the release before, which
|
|
2248
|
+
* ignores the parameter. Optional both ways.
|
|
2249
|
+
*/
|
|
2250
|
+
"/manifest": {
|
|
2251
|
+
GET: {
|
|
2252
|
+
req: {
|
|
2253
|
+
cookies: {
|
|
2254
|
+
val_session: zod.z.string().optional()
|
|
2255
|
+
},
|
|
2256
|
+
headers: {
|
|
2257
|
+
"if-none-match": zod.z.string().optional()
|
|
2258
|
+
},
|
|
2259
|
+
query: {
|
|
2260
|
+
since: onlyOneStringQueryParam.optional()
|
|
2261
|
+
}
|
|
2262
|
+
},
|
|
2263
|
+
res: zod.z.union([unauthorizedResponse, zod.z.object({
|
|
2264
|
+
status: zod.z.literal(500),
|
|
2265
|
+
json: GenericError
|
|
2266
|
+
}), zod.z.object({
|
|
2267
|
+
status: zod.z.literal(200),
|
|
2268
|
+
json: zod.z.object({
|
|
2269
|
+
project: ProjectVersionSchema,
|
|
2270
|
+
modules: zod.z.record(ModuleFilePath, ModuleVersionSchema),
|
|
2271
|
+
/** This answer's ETag, as the header has it. */
|
|
2272
|
+
tag: zod.z.string().optional(),
|
|
2273
|
+
/**
|
|
2274
|
+
* Present on a delta only: the `seq` of the manifest it is
|
|
2275
|
+
* relative to, whose modules that are not named here did not
|
|
2276
|
+
* move. Absent: every module is named.
|
|
2277
|
+
*/
|
|
2278
|
+
since: zod.z.number().optional(),
|
|
2279
|
+
/**
|
|
2280
|
+
* The build that answered is a previous build still answering
|
|
2281
|
+
* (http mode: the content service counted a newer one live).
|
|
2282
|
+
* Its versions are right for its own bases, which the site does
|
|
2283
|
+
* not serve any more: a Studio does not move to them, and asks
|
|
2284
|
+
* again. Optional: absent from a live build and a server of the
|
|
2285
|
+
* release before.
|
|
2286
|
+
*/
|
|
2287
|
+
superseded: zod.z["boolean"]().optional(),
|
|
2288
|
+
/**
|
|
2289
|
+
* The answering build's `val.modules` registry, as an entry a
|
|
2290
|
+
* Studio can import (`valModulesEntryPath`), and the Val it
|
|
2291
|
+
* runs: where a Studio opened on another build takes this
|
|
2292
|
+
* build's code in without reloading the page. Only a build the
|
|
2293
|
+
* platform made and names (`publishBuild`) has one. Optional
|
|
2294
|
+
* both ways: absent from any other server (local development,
|
|
2295
|
+
* where HMR brings the code; a self-hosted deploy) and from a
|
|
2296
|
+
* server of the release before, and ignored by a Studio of the
|
|
2297
|
+
* release before.
|
|
2298
|
+
*/
|
|
2299
|
+
registry: zod.z.object({
|
|
2300
|
+
entry: zod.z.string(),
|
|
2301
|
+
core: zod.z.string()
|
|
2302
|
+
}).optional()
|
|
2303
|
+
})
|
|
2304
|
+
}),
|
|
2305
|
+
/*
|
|
2306
|
+
* Nothing moved since the ETag the client sent. No body on the wire;
|
|
2307
|
+
* the client reads it as `json: null`.
|
|
2308
|
+
*/
|
|
2309
|
+
zod.z.object({
|
|
2310
|
+
status: zod.z.literal(304),
|
|
2311
|
+
json: zod.z["null"]().optional()
|
|
2312
|
+
})])
|
|
2313
|
+
}
|
|
2314
|
+
},
|
|
2055
2315
|
"/stat": {
|
|
2056
2316
|
POST: {
|
|
2057
2317
|
req: {
|
|
2058
2318
|
body: zod.z.object({
|
|
2319
|
+
/**
|
|
2320
|
+
* @deprecated Echoed by the Studio, and compared by the fs and
|
|
2321
|
+
* memory long poll to see that another build answers. This
|
|
2322
|
+
* release's Studio reads each module's version from
|
|
2323
|
+
* `GET /manifest`; goes once the long poll compares the echoed
|
|
2324
|
+
* version instead (see "Release-delayed deletions" in
|
|
2325
|
+
* `docs/plans/one-content-model.md`).
|
|
2326
|
+
*/
|
|
2059
2327
|
sourcesSha: zod.z.string(),
|
|
2328
|
+
/**
|
|
2329
|
+
* @deprecated Echoed by the Studio, and compared by the fs and
|
|
2330
|
+
* memory long poll to see that another build answers. This
|
|
2331
|
+
* release's Studio reads each module's version from
|
|
2332
|
+
* `GET /manifest`; goes once the long poll compares the echoed
|
|
2333
|
+
* version instead (see "Release-delayed deletions" in
|
|
2334
|
+
* `docs/plans/one-content-model.md`).
|
|
2335
|
+
*/
|
|
2060
2336
|
schemaSha: zod.z.string(),
|
|
2061
2337
|
baseSha: zod.z.string(),
|
|
2062
2338
|
patches: zod.z.array(zod.z.string()).optional(),
|
|
@@ -2086,7 +2362,21 @@ var Api = {
|
|
|
2086
2362
|
json: zod.z.union([zod.z.object({
|
|
2087
2363
|
type: zod.z.union([zod.z.literal("request-again"), zod.z.literal("no-change"), zod.z.literal("did-change")]),
|
|
2088
2364
|
baseSha: zod.z.string(),
|
|
2365
|
+
/**
|
|
2366
|
+
* @deprecated Read by a Studio of the release before
|
|
2367
|
+
* (`BaseAlignment`, `SchemaFreshnessWatch`); this release's
|
|
2368
|
+
* only echoes it, and reads each module's version from
|
|
2369
|
+
* `GET /manifest`. Goes in two steps (see "Release-delayed
|
|
2370
|
+
* deletions" in `docs/plans/one-content-model.md`).
|
|
2371
|
+
*/
|
|
2089
2372
|
schemaSha: zod.z.string(),
|
|
2373
|
+
/**
|
|
2374
|
+
* @deprecated Read by a Studio of the release before
|
|
2375
|
+
* (`BaseAlignment`, `SchemaFreshnessWatch`); this release's
|
|
2376
|
+
* only echoes it, and reads each module's version from
|
|
2377
|
+
* `GET /manifest`. Goes in two steps (see "Release-delayed
|
|
2378
|
+
* deletions" in `docs/plans/one-content-model.md`).
|
|
2379
|
+
*/
|
|
2090
2380
|
sourcesSha: zod.z.string(),
|
|
2091
2381
|
patches: zod.z.array(PatchId),
|
|
2092
2382
|
/**
|
|
@@ -2184,13 +2474,34 @@ var Api = {
|
|
|
2184
2474
|
* the git mirror's or the one content reports for the project.
|
|
2185
2475
|
* Absent where there is no history to show.
|
|
2186
2476
|
*/
|
|
2187
|
-
historyBranch: zod.z.string().optional()
|
|
2477
|
+
historyBranch: zod.z.string().optional(),
|
|
2478
|
+
/**
|
|
2479
|
+
* What this answer is relative to: the build that served it
|
|
2480
|
+
* and how far along the project's changes it is (`seq`). Of two
|
|
2481
|
+
* answers, the higher `seq` is the newer. Optional: a server
|
|
2482
|
+
* that predates versions sends none.
|
|
2483
|
+
*/
|
|
2484
|
+
version: ProjectVersionSchema.optional()
|
|
2188
2485
|
}), zod.z.object({
|
|
2189
2486
|
type: zod.z.literal("use-websocket"),
|
|
2190
2487
|
url: zod.z.string(),
|
|
2191
2488
|
nonce: zod.z.string(),
|
|
2192
2489
|
baseSha: zod.z.string(),
|
|
2490
|
+
/**
|
|
2491
|
+
* @deprecated Read by a Studio of the release before
|
|
2492
|
+
* (`BaseAlignment`, `SchemaFreshnessWatch`); this release's
|
|
2493
|
+
* only echoes it, and reads each module's version from
|
|
2494
|
+
* `GET /manifest`. Goes in two steps (see "Release-delayed
|
|
2495
|
+
* deletions" in `docs/plans/one-content-model.md`).
|
|
2496
|
+
*/
|
|
2193
2497
|
schemaSha: zod.z.string(),
|
|
2498
|
+
/**
|
|
2499
|
+
* @deprecated Read by a Studio of the release before
|
|
2500
|
+
* (`BaseAlignment`, `SchemaFreshnessWatch`); this release's
|
|
2501
|
+
* only echoes it, and reads each module's version from
|
|
2502
|
+
* `GET /manifest`. Goes in two steps (see "Release-delayed
|
|
2503
|
+
* deletions" in `docs/plans/one-content-model.md`).
|
|
2504
|
+
*/
|
|
2194
2505
|
sourcesSha: zod.z.string(),
|
|
2195
2506
|
/**
|
|
2196
2507
|
* The commit this deployment was built from.
|
|
@@ -2312,7 +2623,14 @@ var Api = {
|
|
|
2312
2623
|
* the git mirror's or the one content reports for the project.
|
|
2313
2624
|
* Absent where there is no history to show.
|
|
2314
2625
|
*/
|
|
2315
|
-
historyBranch: zod.z.string().optional()
|
|
2626
|
+
historyBranch: zod.z.string().optional(),
|
|
2627
|
+
/**
|
|
2628
|
+
* What this answer is relative to: the build that served it
|
|
2629
|
+
* and how far along the project's changes it is (`seq`). Of two
|
|
2630
|
+
* answers, the higher `seq` is the newer. Optional: a server
|
|
2631
|
+
* that predates versions sends none.
|
|
2632
|
+
*/
|
|
2633
|
+
version: ProjectVersionSchema.optional()
|
|
2316
2634
|
})])
|
|
2317
2635
|
})])
|
|
2318
2636
|
}
|
|
@@ -2354,6 +2672,14 @@ var Api = {
|
|
|
2354
2672
|
path: zod.z.string().optional(),
|
|
2355
2673
|
cookies: {
|
|
2356
2674
|
val_session: zod.z.string().optional()
|
|
2675
|
+
},
|
|
2676
|
+
/*
|
|
2677
|
+
* Carried to content, which answers 304 when it names the answer
|
|
2678
|
+
* (`/project-source` does: valbuild/home sends its `ETag`). The
|
|
2679
|
+
* `ETag` comes back as a header, and a 304 with no body.
|
|
2680
|
+
*/
|
|
2681
|
+
headers: {
|
|
2682
|
+
"if-none-match": zod.z.string().optional()
|
|
2357
2683
|
}
|
|
2358
2684
|
},
|
|
2359
2685
|
/*
|
|
@@ -2365,10 +2691,18 @@ var Api = {
|
|
|
2365
2691
|
* `{ message }` rather than throwing, because a publish failing is
|
|
2366
2692
|
* something the editor has to be told about in words.
|
|
2367
2693
|
*/
|
|
2368
|
-
res: zod.z.object({
|
|
2694
|
+
res: zod.z.union([zod.z.object({
|
|
2369
2695
|
status: zod.z.number(),
|
|
2370
2696
|
json: zod.z.unknown()
|
|
2371
|
-
})
|
|
2697
|
+
}),
|
|
2698
|
+
/*
|
|
2699
|
+
* Content said the caller holds the answer already (`If-None-Match`).
|
|
2700
|
+
* No body on the wire: a 304 cannot carry one.
|
|
2701
|
+
*/
|
|
2702
|
+
zod.z.object({
|
|
2703
|
+
status: zod.z.literal(304),
|
|
2704
|
+
json: zod.z["null"]().optional()
|
|
2705
|
+
})])
|
|
2372
2706
|
},
|
|
2373
2707
|
POST: {
|
|
2374
2708
|
req: {
|
|
@@ -2752,6 +3086,26 @@ var Api = {
|
|
|
2752
3086
|
parentRef: ParentRef,
|
|
2753
3087
|
headVersion: zod.z.number().optional()
|
|
2754
3088
|
})
|
|
3089
|
+
}),
|
|
3090
|
+
/*
|
|
3091
|
+
* The content service failed to take a patch: it answered with a
|
|
3092
|
+
* server error, or could not be reached. Not the 400 above either,
|
|
3093
|
+
* because the patches are fine -- the Studio keeps them and offers
|
|
3094
|
+
* to send them again. `message` is for the editor and `details` (the
|
|
3095
|
+
* status line and the service's own words) for whoever they ask.
|
|
3096
|
+
*
|
|
3097
|
+
* As with the 403, the patches BEFORE the failed one were saved.
|
|
3098
|
+
*/
|
|
3099
|
+
zod.z.object({
|
|
3100
|
+
status: zod.z.literal(502),
|
|
3101
|
+
json: zod.z.object({
|
|
3102
|
+
type: zod.z.literal("service-error"),
|
|
3103
|
+
message: zod.z.string(),
|
|
3104
|
+
details: zod.z.string().optional(),
|
|
3105
|
+
newPatchIds: zod.z.array(PatchId),
|
|
3106
|
+
parentRef: ParentRef,
|
|
3107
|
+
headVersion: zod.z.number().optional()
|
|
3108
|
+
})
|
|
2755
3109
|
}), zod.z.object({
|
|
2756
3110
|
status: zod.z.literal(200),
|
|
2757
3111
|
json: zod.z.object({
|
|
@@ -2787,7 +3141,9 @@ var Api = {
|
|
|
2787
3141
|
status: zod.z.literal(500),
|
|
2788
3142
|
json: zod.z.object({
|
|
2789
3143
|
message: zod.z.string(),
|
|
2790
|
-
error: GenericError
|
|
3144
|
+
error: GenericError,
|
|
3145
|
+
/** What the content service said: its status line and words. */
|
|
3146
|
+
details: zod.z.string().optional()
|
|
2791
3147
|
})
|
|
2792
3148
|
}), zod.z.object({
|
|
2793
3149
|
status: zod.z.literal(200),
|
|
@@ -2804,7 +3160,9 @@ var Api = {
|
|
|
2804
3160
|
})),
|
|
2805
3161
|
baseSha: zod.z.string(),
|
|
2806
3162
|
error: GenericError.optional(),
|
|
2807
|
-
errors: zod.z.record(PatchId, GenericError).optional()
|
|
3163
|
+
errors: zod.z.record(PatchId, GenericError).optional(),
|
|
3164
|
+
/** The project version these patches were read at. See `/stat`. */
|
|
3165
|
+
version: ProjectVersionSchema.optional()
|
|
2808
3166
|
})
|
|
2809
3167
|
})])
|
|
2810
3168
|
}
|
|
@@ -2837,6 +3195,15 @@ var Api = {
|
|
|
2837
3195
|
})])
|
|
2838
3196
|
}
|
|
2839
3197
|
},
|
|
3198
|
+
/**
|
|
3199
|
+
* @deprecated The whole tree. Nothing of this release asks it: the Studio
|
|
3200
|
+
* reads modules by version (`GET /manifest`, `GET /modules/~`), the draft
|
|
3201
|
+
* readers and MCP read the head in-process. Kept, with `exclude_patches`
|
|
3202
|
+
* (`BaseAlignment`'s fetch) and the ignored `validate_sources` and
|
|
3203
|
+
* `validate_binary_files`, for a Studio of the release before; goes in the
|
|
3204
|
+
* release after the one that ships this (see "Release-delayed deletions"
|
|
3205
|
+
* in `docs/plans/one-content-model.md`).
|
|
3206
|
+
*/
|
|
2840
3207
|
"/sources/~": {
|
|
2841
3208
|
PUT: {
|
|
2842
3209
|
req: {
|
|
@@ -2888,9 +3255,97 @@ var Api = {
|
|
|
2888
3255
|
skipped: zod.z.array(PatchId).optional(),
|
|
2889
3256
|
errors: zod.z.record(PatchId, GenericError).optional()
|
|
2890
3257
|
}).optional(),
|
|
2891
|
-
validationErrors: zod.z.record(zod.z.string(), zod.z.array(ValidationError)).optional()
|
|
2892
|
-
|
|
3258
|
+
validationErrors: zod.z.record(zod.z.string(), zod.z.array(ValidationError)).optional(),
|
|
3259
|
+
/**
|
|
3260
|
+
* The version of `source` as answered: with the chain the
|
|
3261
|
+
* server applied, or the empty chain when it applied none
|
|
3262
|
+
* (`apply_patches=false`). Optional: an older server sends
|
|
3263
|
+
* none.
|
|
3264
|
+
*/
|
|
3265
|
+
version: ModuleVersionSchema.optional()
|
|
3266
|
+
})),
|
|
3267
|
+
/** The project version this tree was read at. See `/stat`. */
|
|
3268
|
+
project: ProjectVersionSchema.optional()
|
|
3269
|
+
})
|
|
3270
|
+
})])
|
|
3271
|
+
}
|
|
3272
|
+
},
|
|
3273
|
+
/**
|
|
3274
|
+
* One module by version: phase 6 of `docs/plans/one-content-model.md`
|
|
3275
|
+
* ("The Studio loads everything from one place"). `GET /modules/~<path>`,
|
|
3276
|
+
* with `v` the version wanted as its `moduleKey` (the newest when absent;
|
|
3277
|
+
* a base is a version with the empty chain) and `have` the version the
|
|
3278
|
+
* caller holds: when the caller's chain is a start of the answer's under
|
|
3279
|
+
* the same schema and base, the answer is the patches from it (`delta`),
|
|
3280
|
+
* otherwise the whole module. An answer for an asked `v` is immutable.
|
|
3281
|
+
*/
|
|
3282
|
+
"/modules/~": {
|
|
3283
|
+
GET: {
|
|
3284
|
+
req: {
|
|
3285
|
+
path: zod.z.string(),
|
|
3286
|
+
query: {
|
|
3287
|
+
v: onlyOneStringQueryParam.optional(),
|
|
3288
|
+
have: onlyOneStringQueryParam.optional()
|
|
3289
|
+
},
|
|
3290
|
+
cookies: {
|
|
3291
|
+
val_session: zod.z.string().optional()
|
|
3292
|
+
}
|
|
3293
|
+
},
|
|
3294
|
+
res: zod.z.union([unauthorizedResponse, zod.z.object({
|
|
3295
|
+
status: zod.z.literal(404),
|
|
3296
|
+
json: zod.z.object({
|
|
3297
|
+
message: zod.z.string(),
|
|
3298
|
+
/** The version held now, when the one asked for is not. */
|
|
3299
|
+
current: ModuleVersionSchema.optional()
|
|
2893
3300
|
})
|
|
3301
|
+
}), zod.z.object({
|
|
3302
|
+
status: zod.z.literal(500),
|
|
3303
|
+
json: GenericError
|
|
3304
|
+
}), zod.z.object({
|
|
3305
|
+
status: zod.z.literal(200),
|
|
3306
|
+
headers: zod.z.object({
|
|
3307
|
+
"Cache-Control": zod.z.string()
|
|
3308
|
+
}).optional(),
|
|
3309
|
+
json: zod.z.union([zod.z.object({
|
|
3310
|
+
kind: zod.z.literal("module"),
|
|
3311
|
+
version: ModuleVersionSchema,
|
|
3312
|
+
source: zod.z.any(),
|
|
3313
|
+
/**
|
|
3314
|
+
* The `.jsonValues()` entries the module's chain touched, read,
|
|
3315
|
+
* each with its own version. Every other entry is the base's,
|
|
3316
|
+
* behind its marker in `source`.
|
|
3317
|
+
*/
|
|
3318
|
+
entries: zod.z.record(zod.z.string(), zod.z.union([zod.z.object({
|
|
3319
|
+
content: zod.z.any(),
|
|
3320
|
+
version: EntryVersionSchema
|
|
3321
|
+
}), zod.z.object({
|
|
3322
|
+
deleted: zod.z.literal(true),
|
|
3323
|
+
version: EntryVersionSchema
|
|
3324
|
+
}), zod.z.object({
|
|
3325
|
+
error: zod.z.string(),
|
|
3326
|
+
/** The patch that made it unreadable, when known. */
|
|
3327
|
+
patchId: PatchId.optional(),
|
|
3328
|
+
version: EntryVersionSchema
|
|
3329
|
+
})])),
|
|
3330
|
+
/**
|
|
3331
|
+
* The patch ids its chain is made of, in chain order: what a
|
|
3332
|
+
* client holding this knows the server already has. Optional:
|
|
3333
|
+
* a server of the release before sends none.
|
|
3334
|
+
*/
|
|
3335
|
+
patches: zod.z.array(PatchId).optional(),
|
|
3336
|
+
failed: zod.z.object({
|
|
3337
|
+
patchId: PatchId,
|
|
3338
|
+
message: zod.z.string()
|
|
3339
|
+
}).optional()
|
|
3340
|
+
}), zod.z.object({
|
|
3341
|
+
kind: zod.z.literal("delta"),
|
|
3342
|
+
from: ModuleVersionSchema,
|
|
3343
|
+
to: ModuleVersionSchema,
|
|
3344
|
+
patches: zod.z.array(zod.z.object({
|
|
3345
|
+
patchId: PatchId,
|
|
3346
|
+
patch: Patch
|
|
3347
|
+
}))
|
|
3348
|
+
})])
|
|
2894
3349
|
})])
|
|
2895
3350
|
}
|
|
2896
3351
|
},
|
|
@@ -3614,7 +4069,14 @@ var Api = {
|
|
|
3614
4069
|
path: ModuleFilePath,
|
|
3615
4070
|
key: zod.z.string(),
|
|
3616
4071
|
// The entry's JSON content (or null if the entry has no value).
|
|
3617
|
-
content: zod.z.any()
|
|
4072
|
+
content: zod.z.any(),
|
|
4073
|
+
/**
|
|
4074
|
+
* The version of the record module the entry was read from, with
|
|
4075
|
+
* the chain that was applied (the empty chain without
|
|
4076
|
+
* `apply_patches`). An entry's own version comes with entries
|
|
4077
|
+
* stored as modules of their own.
|
|
4078
|
+
*/
|
|
4079
|
+
version: ModuleVersionSchema.optional()
|
|
3618
4080
|
})
|
|
3619
4081
|
}),
|
|
3620
4082
|
// Shapes (2) and (3): `keys` or `offset`+`limit`.
|
|
@@ -3637,7 +4099,9 @@ var Api = {
|
|
|
3637
4099
|
// All-mode only: the resolved window and the record's total key count.
|
|
3638
4100
|
offset: zod.z.number().optional(),
|
|
3639
4101
|
limit: zod.z.number().optional(),
|
|
3640
|
-
total: zod.z.number().optional()
|
|
4102
|
+
total: zod.z.number().optional(),
|
|
4103
|
+
/** As for a single `key`. */
|
|
4104
|
+
version: ModuleVersionSchema.optional()
|
|
3641
4105
|
})
|
|
3642
4106
|
}), zod.z.object({
|
|
3643
4107
|
status: zod.z.literal(400),
|
|
@@ -4100,8 +4564,8 @@ var createValClient = function createValClient(host, config, options) {
|
|
|
4100
4564
|
body: anyReq.body !== undefined ? JSON.stringify(anyReq.body) : undefined
|
|
4101
4565
|
}).then(/*#__PURE__*/function () {
|
|
4102
4566
|
var _ref2 = createForOfIteratorHelper._asyncToGenerator(/*#__PURE__*/createForOfIteratorHelper._regenerator().m(function _callee(res) {
|
|
4103
|
-
var _apiEndpoint$
|
|
4104
|
-
var contentTypeHeaders, json, valClientResult, message, responseResult;
|
|
4567
|
+
var _apiEndpoint$res2;
|
|
4568
|
+
var contentTypeHeaders, _apiEndpoint$res, notModified, json, valClientResult, message, responseResult;
|
|
4105
4569
|
return createForOfIteratorHelper._regenerator().w(function (_context) {
|
|
4106
4570
|
while (1) switch (_context.n) {
|
|
4107
4571
|
case 0:
|
|
@@ -4119,10 +4583,38 @@ var createValClient = function createValClient(host, config, options) {
|
|
|
4119
4583
|
}
|
|
4120
4584
|
});
|
|
4121
4585
|
case 1:
|
|
4122
|
-
if (
|
|
4586
|
+
if (!(res.status === 304)) {
|
|
4587
|
+
_context.n = 3;
|
|
4588
|
+
break;
|
|
4589
|
+
}
|
|
4590
|
+
notModified = {
|
|
4591
|
+
status: 304,
|
|
4592
|
+
json: null
|
|
4593
|
+
};
|
|
4594
|
+
if (!(((_apiEndpoint$res = apiEndpoint.res) === null || _apiEndpoint$res === void 0 ? void 0 : _apiEndpoint$res.safeParse(notModified).success) === false)) {
|
|
4123
4595
|
_context.n = 2;
|
|
4124
4596
|
break;
|
|
4125
4597
|
}
|
|
4598
|
+
return _context.a(2, {
|
|
4599
|
+
status: null,
|
|
4600
|
+
json: {
|
|
4601
|
+
type: "client_side_validation_error",
|
|
4602
|
+
message: "Invalid response (304 from a route that does not answer one). This could be a result of mismatched Val versions.",
|
|
4603
|
+
details: {
|
|
4604
|
+
validationError: "Unexpected 304",
|
|
4605
|
+
data: {
|
|
4606
|
+
status: res.status
|
|
4607
|
+
}
|
|
4608
|
+
}
|
|
4609
|
+
}
|
|
4610
|
+
});
|
|
4611
|
+
case 2:
|
|
4612
|
+
return _context.a(2, notModified);
|
|
4613
|
+
case 3:
|
|
4614
|
+
if (contentTypeHeaders !== null && contentTypeHeaders !== void 0 && contentTypeHeaders.includes("application/json")) {
|
|
4615
|
+
_context.n = 4;
|
|
4616
|
+
break;
|
|
4617
|
+
}
|
|
4126
4618
|
return _context.a(2, {
|
|
4127
4619
|
status: null,
|
|
4128
4620
|
json: {
|
|
@@ -4137,17 +4629,17 @@ var createValClient = function createValClient(host, config, options) {
|
|
|
4137
4629
|
}
|
|
4138
4630
|
}
|
|
4139
4631
|
});
|
|
4140
|
-
case
|
|
4141
|
-
_context.n =
|
|
4632
|
+
case 4:
|
|
4633
|
+
_context.n = 5;
|
|
4142
4634
|
return res.json();
|
|
4143
|
-
case
|
|
4635
|
+
case 5:
|
|
4144
4636
|
json = _context.v;
|
|
4145
4637
|
valClientResult = {
|
|
4146
4638
|
status: res.status,
|
|
4147
4639
|
json: json
|
|
4148
4640
|
};
|
|
4149
4641
|
if (!(res.status === 500)) {
|
|
4150
|
-
_context.n =
|
|
4642
|
+
_context.n = 6;
|
|
4151
4643
|
break;
|
|
4152
4644
|
}
|
|
4153
4645
|
core.Internal.log.error("Server responded with an error", json);
|
|
@@ -4159,10 +4651,10 @@ var createValClient = function createValClient(host, config, options) {
|
|
|
4159
4651
|
message: message
|
|
4160
4652
|
}
|
|
4161
4653
|
});
|
|
4162
|
-
case
|
|
4163
|
-
responseResult = (_apiEndpoint$
|
|
4654
|
+
case 6:
|
|
4655
|
+
responseResult = (_apiEndpoint$res2 = apiEndpoint.res) === null || _apiEndpoint$res2 === void 0 ? void 0 : _apiEndpoint$res2.safeParse(valClientResult);
|
|
4164
4656
|
if (!(responseResult && !responseResult.success)) {
|
|
4165
|
-
_context.n =
|
|
4657
|
+
_context.n = 7;
|
|
4166
4658
|
break;
|
|
4167
4659
|
}
|
|
4168
4660
|
return _context.a(2, {
|
|
@@ -4176,7 +4668,7 @@ var createValClient = function createValClient(host, config, options) {
|
|
|
4176
4668
|
}
|
|
4177
4669
|
}
|
|
4178
4670
|
});
|
|
4179
|
-
case
|
|
4671
|
+
case 7:
|
|
4180
4672
|
return _context.a(2, {
|
|
4181
4673
|
status: res.status,
|
|
4182
4674
|
json: json
|
|
@@ -4320,6 +4812,44 @@ var urlOf = function urlOf() {
|
|
|
4320
4812
|
return "".concat(route);
|
|
4321
4813
|
};
|
|
4322
4814
|
|
|
4815
|
+
/**
|
|
4816
|
+
* Which Val package, at which version, sent a request to Val Build.
|
|
4817
|
+
*
|
|
4818
|
+
* Sent on every request a Val package makes to Val's own services from a
|
|
4819
|
+
* server or a CLI -- the content host, `/api/login`, the analytics ingest --
|
|
4820
|
+
* so Val Build can tell which versions are still out there and which old
|
|
4821
|
+
* endpoints they still call. Without it an endpoint can never be retired with
|
|
4822
|
+
* any confidence: a request from a site nobody has upgraded in a year looks
|
|
4823
|
+
* exactly like one from yesterday's release.
|
|
4824
|
+
*
|
|
4825
|
+
* The value is a product token, `@valbuild/server@0.152.0`: the package whose
|
|
4826
|
+
* code built the request, because that is what decides which endpoints are
|
|
4827
|
+
* called. Not the framework binding or the app: those do not choose URLs.
|
|
4828
|
+
*
|
|
4829
|
+
* NOT sent from the browser. A custom header on a cross-origin request makes
|
|
4830
|
+
* the browser send a CORS preflight, and a host that does not list the header
|
|
4831
|
+
* in `Access-Control-Allow-Headers` refuses the request outright -- so adding
|
|
4832
|
+
* it there is a change to every such host first.
|
|
4833
|
+
*
|
|
4834
|
+
* It identifies the code, not the caller: anyone can send any value, so it is
|
|
4835
|
+
* for counting, never for deciding what a request may do.
|
|
4836
|
+
*/
|
|
4837
|
+
var VAL_CLIENT_HEADER = "x-val-client";
|
|
4838
|
+
|
|
4839
|
+
/** Packages that send {@link VAL_CLIENT_HEADER}. */
|
|
4840
|
+
|
|
4841
|
+
/**
|
|
4842
|
+
* `{ "x-val-client": "@valbuild/server@0.152.0" }`, to spread into a
|
|
4843
|
+
* request's headers.
|
|
4844
|
+
*
|
|
4845
|
+
* A version that could not be read is sent as `unknown` rather than leaving
|
|
4846
|
+
* the header out: "a server too broken to know its version" is still a thing
|
|
4847
|
+
* worth counting, and it is not the same as "a version older than the header".
|
|
4848
|
+
*/
|
|
4849
|
+
function valClientHeaders(pkg, version) {
|
|
4850
|
+
return createForOfIteratorHelper._defineProperty({}, VAL_CLIENT_HEADER, "".concat(pkg, "@").concat(version || "unknown"));
|
|
4851
|
+
}
|
|
4852
|
+
|
|
4323
4853
|
/**
|
|
4324
4854
|
* Can this value, taken from one schema, be written into another?
|
|
4325
4855
|
*
|
|
@@ -4753,10 +5283,1504 @@ function chainHeadOf(reportedHead, listed) {
|
|
|
4753
5283
|
return (_listed = listed[listed.length - 1]) !== null && _listed !== void 0 ? _listed : null;
|
|
4754
5284
|
}
|
|
4755
5285
|
|
|
4756
|
-
|
|
4757
|
-
|
|
4758
|
-
|
|
4759
|
-
|
|
5286
|
+
/**
|
|
5287
|
+
* Where a build's `val.modules` registry can be imported from: phase 9 of
|
|
5288
|
+
* `docs/plans/one-content-model.md`, "Reloading the schema while the Studio
|
|
5289
|
+
* runs".
|
|
5290
|
+
*
|
|
5291
|
+
* A build made by `@valbuild/tanstack-build` (every build the platform
|
|
5292
|
+
* serves) emits its `val.modules` as an ES module of its own beside its
|
|
5293
|
+
* client chunks, named after the build: `/_app/val-modules-<build>.js`,
|
|
5294
|
+
* whose default export is the registry. The server running that build knows
|
|
5295
|
+
* its name (`VAL_BUILD`, Val's `publishBuild`) and names the entry on
|
|
5296
|
+
* `GET /manifest` (`registry`), so a Studio opened on an older build imports
|
|
5297
|
+
* the new one's code and reloads it in place (`HostStore.reloadCode`)
|
|
5298
|
+
* instead of being told to reload the page.
|
|
5299
|
+
*
|
|
5300
|
+
* The convention is the builder's and this file's together: the builder
|
|
5301
|
+
* writes the file (`valModulesEntryFile` in `@valbuild/tanstack-build`, which
|
|
5302
|
+
* does not depend on this package), and each side pins the same literal,
|
|
5303
|
+
* `val-modules-<build>.js` (`buildRegistry.test.ts` here, `valModules.test.ts`
|
|
5304
|
+
* there).
|
|
5305
|
+
*/
|
|
5306
|
+
|
|
5307
|
+
/** The file a build's registry is emitted as, among its client chunks. */
|
|
5308
|
+
function valModulesEntryFile(build) {
|
|
5309
|
+
return "val-modules-".concat(build, ".js");
|
|
5310
|
+
}
|
|
5311
|
+
|
|
5312
|
+
/** Where a build served by the platform serves its registry. */
|
|
5313
|
+
function valModulesEntryPath(build) {
|
|
5314
|
+
return "/_app/".concat(valModulesEntryFile(build));
|
|
5315
|
+
}
|
|
5316
|
+
|
|
5317
|
+
/**
|
|
5318
|
+
* What `GET /manifest` says about the code of the build that answered:
|
|
5319
|
+
* where its registry is, and which Val it runs. A Studio running another
|
|
5320
|
+
* Val cannot take the build's code in (its own stores would run against a
|
|
5321
|
+
* new core), so it reloads itself instead (`other-val-version`).
|
|
5322
|
+
*/
|
|
5323
|
+
|
|
5324
|
+
/*
|
|
5325
|
+
* How a patch op relates to a `.jsonValues()` record: which ops reach into an
|
|
5326
|
+
* entry, what a write of the whole record says about each entry, and how an
|
|
5327
|
+
* op inside an entry is rebased onto the entry's own file.
|
|
5328
|
+
*
|
|
5329
|
+
* Here, in shared, because the engine (`applyChain.ts`) is built on them and
|
|
5330
|
+
* runs wherever content is patched; the commit flow (`ValOps.prepare`), the
|
|
5331
|
+
* CLI's fixes and the language server use the same functions, through
|
|
5332
|
+
* `@valbuild/server`, which re-exports them. One rule, one implementation:
|
|
5333
|
+
* a draft that shows one thing while publishing writes another is the failure
|
|
5334
|
+
* nobody sees until it is published.
|
|
5335
|
+
*/
|
|
5336
|
+
|
|
5337
|
+
/**
|
|
5338
|
+
* Classification of a single patch op against a module's serialized schema,
|
|
5339
|
+
* used by the commit flow to route ops for `.jsonValues()` records:
|
|
5340
|
+
*
|
|
5341
|
+
* - `normal`: the op does not descend into a `.jsonValues()` entry; apply it to
|
|
5342
|
+
* the `.val.ts` as usual.
|
|
5343
|
+
* - `entry`: the op targets a `.jsonValues()` entry. `recordPath` is the path to
|
|
5344
|
+
* the record within the module source (empty for a root record/router),
|
|
5345
|
+
* `entryKey` is the entry key, and `subPath` is the remaining path inside the
|
|
5346
|
+
* entry (empty when the op targets the entry value itself, e.g. add/remove of
|
|
5347
|
+
* the whole entry).
|
|
5348
|
+
*/
|
|
5349
|
+
|
|
5350
|
+
/**
|
|
5351
|
+
* Walks the serialized schema following the op path. When a `.jsonValues()`
|
|
5352
|
+
* record is encountered, the next path segment is the entry key and everything
|
|
5353
|
+
* after it lives inside the entry's `*.val.json` (so it does not touch the
|
|
5354
|
+
* `.val.ts`). Returns `{ kind: "normal" }` when the op never enters a
|
|
5355
|
+
* `.jsonValues()` record.
|
|
5356
|
+
*/
|
|
5357
|
+
function classifyJsonValuesOp(schema, opPath) {
|
|
5358
|
+
var current = schema;
|
|
5359
|
+
var recordPath = [];
|
|
5360
|
+
for (var i = 0; i < opPath.length; i++) {
|
|
5361
|
+
if (!current) {
|
|
5362
|
+
return {
|
|
5363
|
+
kind: "normal"
|
|
5364
|
+
};
|
|
5365
|
+
}
|
|
5366
|
+
if (current.type === "record" && current.jsonValues) {
|
|
5367
|
+
return {
|
|
5368
|
+
kind: "entry",
|
|
5369
|
+
recordPath: recordPath.slice(),
|
|
5370
|
+
entryKey: opPath[i],
|
|
5371
|
+
subPath: opPath.slice(i + 1)
|
|
5372
|
+
};
|
|
5373
|
+
}
|
|
5374
|
+
var seg = opPath[i];
|
|
5375
|
+
current = descend(current, seg);
|
|
5376
|
+
recordPath.push(seg);
|
|
5377
|
+
}
|
|
5378
|
+
return {
|
|
5379
|
+
kind: "normal"
|
|
5380
|
+
};
|
|
5381
|
+
}
|
|
5382
|
+
function descend(schema, key) {
|
|
5383
|
+
switch (schema.type) {
|
|
5384
|
+
case "object":
|
|
5385
|
+
return schema.items[key];
|
|
5386
|
+
case "record":
|
|
5387
|
+
return schema.item;
|
|
5388
|
+
case "array":
|
|
5389
|
+
return schema.item;
|
|
5390
|
+
default:
|
|
5391
|
+
// Unions / primitives / leaf schemas: we cannot (or need not) descend
|
|
5392
|
+
// further to find a jsonValues record. Anything below is a normal
|
|
5393
|
+
// `.val.ts` edit.
|
|
5394
|
+
return undefined;
|
|
5395
|
+
}
|
|
5396
|
+
}
|
|
5397
|
+
|
|
5398
|
+
/**
|
|
5399
|
+
* Is this op a write of the WHOLE record at the root of a `.jsonValues()`
|
|
5400
|
+
* module?
|
|
5401
|
+
*
|
|
5402
|
+
* The one op {@link classifyJsonValuesOp} cannot classify: it finds the entry
|
|
5403
|
+
* key by walking the op path, and the root path has no segments to walk, so a
|
|
5404
|
+
* root write reads as `normal` and gets applied to the `.val.ts` - writing
|
|
5405
|
+
* markers over the `c.json(() => import(...))` calls that make the entries load
|
|
5406
|
+
* at all. {@link expandJsonValuesRootOp} turns it into ops that DO name a key,
|
|
5407
|
+
* which everything downstream already handles.
|
|
5408
|
+
*
|
|
5409
|
+
* `add` counts as well as `replace`: at the root both mean "the document is now
|
|
5410
|
+
* this" (see `JSONOps`), so both have to be expanded or the unexpanded one is
|
|
5411
|
+
* the same bug again.
|
|
5412
|
+
*
|
|
5413
|
+
* The value has to BE a record, and that is part of the question rather than a
|
|
5414
|
+
* check inside the expansion: a `.jsonValues()` record can be nullable, and
|
|
5415
|
+
* `null` at the root means the module no longer has a record at all - there are
|
|
5416
|
+
* no entries to write, and it is an ordinary `.val.ts` write, exactly as it was
|
|
5417
|
+
* before any of this existed. Same for any other non-record value: not a
|
|
5418
|
+
* whole-record write, so not this conversion's to route.
|
|
5419
|
+
*/
|
|
5420
|
+
function isJsonValuesRootOp(schema, op) {
|
|
5421
|
+
return (op.op === "replace" || op.op === "add") && op.path.length === 0 && isJsonValuesRecord(op.value) && schema.type === "record" && schema.jsonValues === true;
|
|
5422
|
+
}
|
|
5423
|
+
function isJsonValuesRecord(value) {
|
|
5424
|
+
return createForOfIteratorHelper._typeof(value) === "object" && value !== null && !Array.isArray(value);
|
|
5425
|
+
}
|
|
5426
|
+
|
|
5427
|
+
/**
|
|
5428
|
+
* One entry as it stands right now, for {@link expandJsonValuesRootOp} to
|
|
5429
|
+
* expand against.
|
|
5430
|
+
*
|
|
5431
|
+
* The key set decides add-vs-replace-vs-remove. The content, where the caller
|
|
5432
|
+
* has it, decides whether an op is emitted at all: an entry whose content is
|
|
5433
|
+
* already what the root write says is left alone rather than rewritten, so
|
|
5434
|
+
* putting a module back does not touch every `*.val.json` it did not change.
|
|
5435
|
+
* `undefined` means "this entry exists, content unknown" - which is what the
|
|
5436
|
+
* Studio's draft source has, because the source holds markers - and yields a
|
|
5437
|
+
* `replace`, the safe answer.
|
|
5438
|
+
*/
|
|
5439
|
+
|
|
5440
|
+
/**
|
|
5441
|
+
* Fans a whole-record write at a `.jsonValues()` module's root out into ops
|
|
5442
|
+
* that each name an entry key.
|
|
5443
|
+
*
|
|
5444
|
+
* This is THE conversion, and it has exactly one implementation on purpose: the
|
|
5445
|
+
* commit flow (`ValOps.prepare`) and the engine that builds draft content
|
|
5446
|
+
* (`applyPatchRecord`) both expand through here, so a draft
|
|
5447
|
+
* cannot show something other than what publishing writes. A second
|
|
5448
|
+
* implementation of the same rule would differ silently, which is the whole
|
|
5449
|
+
* failure mode this exists to prevent.
|
|
5450
|
+
*
|
|
5451
|
+
* The value must be the entries' CONTENT. A module's Source is markers, not
|
|
5452
|
+
* content, so a Source handed over here is refused rather than written: that is
|
|
5453
|
+
* the shape of the original bug (a revert replaying the archived Source), and
|
|
5454
|
+
* it is not recoverable afterwards - the markers replace the entries' content
|
|
5455
|
+
* on disk.
|
|
5456
|
+
*/
|
|
5457
|
+
function expandJsonValuesRootOp(op, currentEntries) {
|
|
5458
|
+
var valueRes = jsonValuesRootValue(op);
|
|
5459
|
+
if (fp.result.isErr(valueRes)) {
|
|
5460
|
+
return valueRes;
|
|
5461
|
+
}
|
|
5462
|
+
var value = valueRes.value;
|
|
5463
|
+
var expanded = [];
|
|
5464
|
+
for (var _i = 0, _Object$keys = Object.keys(value); _i < _Object$keys.length; _i++) {
|
|
5465
|
+
var entryKey = _Object$keys[_i];
|
|
5466
|
+
var write = writeForEntry(value, entryKey, currentEntries);
|
|
5467
|
+
if (write !== null) {
|
|
5468
|
+
expanded.push(write);
|
|
5469
|
+
}
|
|
5470
|
+
}
|
|
5471
|
+
var _iterator = createForOfIteratorHelper._createForOfIteratorHelper(currentEntries.keys()),
|
|
5472
|
+
_step;
|
|
5473
|
+
try {
|
|
5474
|
+
for (_iterator.s(); !(_step = _iterator.n()).done;) {
|
|
5475
|
+
var _entryKey = _step.value;
|
|
5476
|
+
if (!Object.prototype.hasOwnProperty.call(value, _entryKey)) {
|
|
5477
|
+
expanded.push({
|
|
5478
|
+
op: "remove",
|
|
5479
|
+
path: [_entryKey]
|
|
5480
|
+
});
|
|
5481
|
+
}
|
|
5482
|
+
}
|
|
5483
|
+
} catch (err) {
|
|
5484
|
+
_iterator.e(err);
|
|
5485
|
+
} finally {
|
|
5486
|
+
_iterator.f();
|
|
5487
|
+
}
|
|
5488
|
+
return fp.result.ok(expanded);
|
|
5489
|
+
}
|
|
5490
|
+
|
|
5491
|
+
/**
|
|
5492
|
+
* What a whole-record write says about ONE entry: the same rule
|
|
5493
|
+
* {@link expandJsonValuesRootOp} applies to all of them.
|
|
5494
|
+
*
|
|
5495
|
+
* For a reader that is about one entry. Expanding the whole record to find the
|
|
5496
|
+
* one op that names its key costs an op per entry per entry read - the record
|
|
5497
|
+
* squared, on exactly the large records this feature is for. The rule itself is
|
|
5498
|
+
* `writeForEntry`, shared with the expansion above, so the two cannot come to
|
|
5499
|
+
* disagree; only the loop around it differs.
|
|
5500
|
+
*
|
|
5501
|
+
* `null` means the write says nothing about this entry: it holds that content
|
|
5502
|
+
* already, or the record does not name it and it does not exist.
|
|
5503
|
+
*/
|
|
5504
|
+
function expandJsonValuesRootOpForKey(op, entryKey, currentEntries) {
|
|
5505
|
+
var valueRes = jsonValuesRootValue(op);
|
|
5506
|
+
if (fp.result.isErr(valueRes)) {
|
|
5507
|
+
return valueRes;
|
|
5508
|
+
}
|
|
5509
|
+
var value = valueRes.value;
|
|
5510
|
+
if (Object.prototype.hasOwnProperty.call(value, entryKey)) {
|
|
5511
|
+
return fp.result.ok(writeForEntry(value, entryKey, currentEntries));
|
|
5512
|
+
}
|
|
5513
|
+
if (currentEntries.has(entryKey)) {
|
|
5514
|
+
return fp.result.ok({
|
|
5515
|
+
op: "remove",
|
|
5516
|
+
path: [entryKey]
|
|
5517
|
+
});
|
|
5518
|
+
}
|
|
5519
|
+
return fp.result.ok(null);
|
|
5520
|
+
}
|
|
5521
|
+
|
|
5522
|
+
/**
|
|
5523
|
+
* The record a whole-record write carries, once it is known to be one.
|
|
5524
|
+
*
|
|
5525
|
+
* Every entry's value is checked here rather than where each one is written:
|
|
5526
|
+
* a marker anywhere in the record means the writer handed over a module's
|
|
5527
|
+
* Source, and the answer to that is to refuse the write, not to write the rest
|
|
5528
|
+
* of it. Both entry points ask, so an entry read reports it too rather than
|
|
5529
|
+
* showing a draft of a patch that publishing will refuse.
|
|
5530
|
+
*/
|
|
5531
|
+
function jsonValuesRootValue(op) {
|
|
5532
|
+
if (op.op !== "replace" && op.op !== "add") {
|
|
5533
|
+
return fp.result.err(new patch.PatchError("Cannot '".concat(op.op, "' the root of a .jsonValues() record: only add and replace write the whole record")));
|
|
5534
|
+
}
|
|
5535
|
+
var value = op.value;
|
|
5536
|
+
if (!isJsonValuesRecord(value)) {
|
|
5537
|
+
return fp.result.err(new patch.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"));
|
|
5538
|
+
}
|
|
5539
|
+
for (var _i2 = 0, _Object$keys2 = Object.keys(value); _i2 < _Object$keys2.length; _i2++) {
|
|
5540
|
+
var entryKey = _Object$keys2[_i2];
|
|
5541
|
+
/*
|
|
5542
|
+
* A marker is what a module's Source holds WHERE THE CONTENT IS NOT: it
|
|
5543
|
+
* says the content is in the entry's own file. Handed back as an entry's
|
|
5544
|
+
* value - which is what replaying an archived Source does - it would
|
|
5545
|
+
* replace that content with the note saying where it used to be, and
|
|
5546
|
+
* nothing downstream could tell the difference.
|
|
5547
|
+
*/
|
|
5548
|
+
if (core.Internal.isJson(value[entryKey])) {
|
|
5549
|
+
return fp.result.err(new patch.PatchError("Cannot write the .jsonValues() entry '".concat(entryKey, "' from a '").concat(core.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")));
|
|
5550
|
+
}
|
|
5551
|
+
}
|
|
5552
|
+
return fp.result.ok(value);
|
|
5553
|
+
}
|
|
5554
|
+
|
|
5555
|
+
/** THE rule, for one entry the record names. `null` = nothing to write. */
|
|
5556
|
+
function writeForEntry(value, entryKey, currentEntries) {
|
|
5557
|
+
var content = value[entryKey];
|
|
5558
|
+
if (!currentEntries.has(entryKey)) {
|
|
5559
|
+
return {
|
|
5560
|
+
op: "add",
|
|
5561
|
+
path: [entryKey],
|
|
5562
|
+
value: content
|
|
5563
|
+
};
|
|
5564
|
+
}
|
|
5565
|
+
var current = currentEntries.get(entryKey);
|
|
5566
|
+
if (current !== undefined && patch.deepEqual(current, content)) {
|
|
5567
|
+
// Unchanged: emitting a replace here would rewrite the entry's file with
|
|
5568
|
+
// the bytes it already holds, so putting a module back would show up as a
|
|
5569
|
+
// change to every entry in it.
|
|
5570
|
+
return null;
|
|
5571
|
+
}
|
|
5572
|
+
return {
|
|
5573
|
+
op: "replace",
|
|
5574
|
+
path: [entryKey],
|
|
5575
|
+
value: content
|
|
5576
|
+
};
|
|
5577
|
+
}
|
|
5578
|
+
|
|
5579
|
+
/**
|
|
5580
|
+
* Rebases a patch op that targets a `.jsonValues()` entry's content so its paths
|
|
5581
|
+
* are relative to the entry's `*.val.json` root (drops the record + entry-key
|
|
5582
|
+
* prefix). Used to replay the op against the backing JSON file.
|
|
5583
|
+
*/
|
|
5584
|
+
function rebaseContentOp(op, prefixLen) {
|
|
5585
|
+
var path = op.path.slice(prefixLen);
|
|
5586
|
+
switch (op.op) {
|
|
5587
|
+
case "add":
|
|
5588
|
+
case "replace":
|
|
5589
|
+
case "test":
|
|
5590
|
+
return fp.result.ok(createForOfIteratorHelper._objectSpread2(createForOfIteratorHelper._objectSpread2({}, op), {}, {
|
|
5591
|
+
path: path
|
|
5592
|
+
}));
|
|
5593
|
+
case "remove":
|
|
5594
|
+
{
|
|
5595
|
+
if (!fp.array.isNonEmpty(path)) {
|
|
5596
|
+
return fp.result.err(new patch.PatchError("Cannot remove the root of a jsonValues entry"));
|
|
5597
|
+
}
|
|
5598
|
+
return fp.result.ok(createForOfIteratorHelper._objectSpread2(createForOfIteratorHelper._objectSpread2({}, op), {}, {
|
|
5599
|
+
path: path
|
|
5600
|
+
}));
|
|
5601
|
+
}
|
|
5602
|
+
case "move":
|
|
5603
|
+
{
|
|
5604
|
+
var from = op.from.slice(prefixLen);
|
|
5605
|
+
if (!fp.array.isNonEmpty(from)) {
|
|
5606
|
+
return fp.result.err(new patch.PatchError("Cannot move from the root of a jsonValues entry"));
|
|
5607
|
+
}
|
|
5608
|
+
return fp.result.ok(createForOfIteratorHelper._objectSpread2(createForOfIteratorHelper._objectSpread2({}, op), {}, {
|
|
5609
|
+
path: path,
|
|
5610
|
+
from: from
|
|
5611
|
+
}));
|
|
5612
|
+
}
|
|
5613
|
+
case "copy":
|
|
5614
|
+
return fp.result.ok(createForOfIteratorHelper._objectSpread2(createForOfIteratorHelper._objectSpread2({}, op), {}, {
|
|
5615
|
+
path: path,
|
|
5616
|
+
from: op.from.slice(prefixLen)
|
|
5617
|
+
}));
|
|
5618
|
+
case "file":
|
|
5619
|
+
// A file op carries bytes, not content. The binary goes through the
|
|
5620
|
+
// normal upload pipeline; what lands in the entry is the `patch_id` that
|
|
5621
|
+
// marks the bytes as not yet committed, which the caller writes.
|
|
5622
|
+
return fp.result.err(new patch.PatchError("A file op is not a content op: rebase it via its path"));
|
|
5623
|
+
}
|
|
5624
|
+
}
|
|
5625
|
+
|
|
5626
|
+
/**
|
|
5627
|
+
* The entry a path names when it names a WHOLE entry of the root
|
|
5628
|
+
* `.jsonValues()` record, and `undefined` for anything else.
|
|
5629
|
+
*/
|
|
5630
|
+
function wholeEntryKey(serializedSchema, path) {
|
|
5631
|
+
if (!serializedSchema) {
|
|
5632
|
+
return undefined;
|
|
5633
|
+
}
|
|
5634
|
+
var cls = classifyJsonValuesOp(serializedSchema, path);
|
|
5635
|
+
return cls.kind === "entry" && cls.recordPath.length === 0 && cls.subPath.length === 0 ? cls.entryKey : undefined;
|
|
5636
|
+
}
|
|
5637
|
+
|
|
5638
|
+
/*
|
|
5639
|
+
* THE engine: one function that turns a module's content and a patch into
|
|
5640
|
+
* the module's next content. See `docs/plans/one-content-model.md`, "One
|
|
5641
|
+
* engine".
|
|
5642
|
+
*
|
|
5643
|
+
* It owns the rules the server's two readers used to follow each in their own
|
|
5644
|
+
* way (`ValOps.getSources` for the module, and a replay of its own for a
|
|
5645
|
+
* `.jsonValues()` entry, kept test-only in `legacyEngine.ts` in
|
|
5646
|
+
* `@valbuild/server` for the conformance cases):
|
|
5647
|
+
*
|
|
5648
|
+
* - where a file's `patch_id` lands: the field's path plus `nestedFilePath`,
|
|
5649
|
+
* after every other op of the patch;
|
|
5650
|
+
* - what an edit inside an entry does: applied to the entry's content when it
|
|
5651
|
+
* is loaded, held (`waiting`) until it is when it is not;
|
|
5652
|
+
* - what a whole entry added does: a marker in the module, content in the
|
|
5653
|
+
* entry;
|
|
5654
|
+
* - that a module's chain stops at its first failing patch, and that the
|
|
5655
|
+
* entries stop with it.
|
|
5656
|
+
*
|
|
5657
|
+
* Copy-on-write along each op's path: nothing is mutated, so a subtree no op
|
|
5658
|
+
* touched keeps its identity (which makes "did this path change?" a `===`),
|
|
5659
|
+
* and a patch costs the depth of its path, not the module's size. Every
|
|
5660
|
+
* container it copies is counted ({@link engineWork}).
|
|
5661
|
+
*/
|
|
5662
|
+
|
|
5663
|
+
/** What one `.jsonValues()` entry is at a point in a module's chain. */
|
|
5664
|
+
|
|
5665
|
+
/**
|
|
5666
|
+
* The ops held for an entry, newest first: a list that is extended without
|
|
5667
|
+
* copying, so a chain of N edits to an entry nobody loaded costs N, not N².
|
|
5668
|
+
*/
|
|
5669
|
+
|
|
5670
|
+
/**
|
|
5671
|
+
* A module's content: its source, and what its `.jsonValues()` entries are.
|
|
5672
|
+
*
|
|
5673
|
+
* `entries` holds only the entries the chain has touched. An entry it has
|
|
5674
|
+
* not is the base's: `waiting` on its own key with nothing held, as long as
|
|
5675
|
+
* the source has its marker ({@link entryState}). So a module with no
|
|
5676
|
+
* entries, or a chain that never touches one, costs nothing here.
|
|
5677
|
+
*/
|
|
5678
|
+
|
|
5679
|
+
/** One patch of a module's chain. */
|
|
5680
|
+
|
|
5681
|
+
var NO_ENTRIES = new Map();
|
|
5682
|
+
|
|
5683
|
+
/** A module's content with nothing applied yet: its base. */
|
|
5684
|
+
function baseModuleContent(source) {
|
|
5685
|
+
return {
|
|
5686
|
+
source: source,
|
|
5687
|
+
entries: NO_ENTRIES
|
|
5688
|
+
};
|
|
5689
|
+
}
|
|
5690
|
+
|
|
5691
|
+
// #region counted work
|
|
5692
|
+
|
|
5693
|
+
/**
|
|
5694
|
+
* What a copied container costs, per slot copied (an object's entry or an
|
|
5695
|
+
* array's element): one reference. Strings and numbers are shared, never
|
|
5696
|
+
* copied, so this is what a copy really moves -- and what the whole module
|
|
5697
|
+
* cost per patch before copy-on-write (`applyPatch(deepClone(module))`).
|
|
5698
|
+
*/
|
|
5699
|
+
var SLOT_BYTES = 8;
|
|
5700
|
+
var applies = 0;
|
|
5701
|
+
var clonedBytes = 0;
|
|
5702
|
+
|
|
5703
|
+
/**
|
|
5704
|
+
* The work the engine has done in this process: patches applied
|
|
5705
|
+
* (`engine:apply-patch`) and bytes copied (`engine:clone-bytes`, see
|
|
5706
|
+
* {@link SLOT_BYTES}). A test takes it before and after; never reset, so two
|
|
5707
|
+
* readers of it cannot disturb each other.
|
|
5708
|
+
*/
|
|
5709
|
+
function engineWork() {
|
|
5710
|
+
return {
|
|
5711
|
+
applyPatch: applies,
|
|
5712
|
+
cloneBytes: clonedBytes
|
|
5713
|
+
};
|
|
5714
|
+
}
|
|
5715
|
+
|
|
5716
|
+
// #region the patch
|
|
5717
|
+
|
|
5718
|
+
/**
|
|
5719
|
+
* Applies one patch to a module's content. Nothing is mutated: the module
|
|
5720
|
+
* passed in is still what it was, and the one returned shares every subtree
|
|
5721
|
+
* the patch did not touch.
|
|
5722
|
+
*
|
|
5723
|
+
* `failed` means the patch does not apply, and then none of it does: half a
|
|
5724
|
+
* patch is half an edit. The caller stops the module's chain there.
|
|
5725
|
+
*
|
|
5726
|
+
* `schema` is the module's serialized schema: it says which ops reach into a
|
|
5727
|
+
* `.jsonValues()` entry. Without one, every op is an op on the source.
|
|
5728
|
+
*/
|
|
5729
|
+
function applyPatchRecord(module, record, schema) {
|
|
5730
|
+
applies++;
|
|
5731
|
+
var patchId = record.patchId;
|
|
5732
|
+
var fail = function fail(message) {
|
|
5733
|
+
return {
|
|
5734
|
+
status: "failed",
|
|
5735
|
+
patchId: patchId,
|
|
5736
|
+
message: message
|
|
5737
|
+
};
|
|
5738
|
+
};
|
|
5739
|
+
var source = module.source;
|
|
5740
|
+
var entries = null;
|
|
5741
|
+
var stateOf = function stateOf(key) {
|
|
5742
|
+
var _get;
|
|
5743
|
+
return (_get = (entries !== null && entries !== void 0 ? entries : module.entries).get(key)) !== null && _get !== void 0 ? _get : implicitState(source, key);
|
|
5744
|
+
};
|
|
5745
|
+
var setEntry = function setEntry(key, state) {
|
|
5746
|
+
if (entries === null) {
|
|
5747
|
+
clonedBytes += SLOT_BYTES * module.entries.size;
|
|
5748
|
+
entries = new Map(module.entries);
|
|
5749
|
+
}
|
|
5750
|
+
entries.set(key, state);
|
|
5751
|
+
};
|
|
5752
|
+
/** Sets it, unless the entry already failed: an entry's error is sticky. */
|
|
5753
|
+
var settle = function settle(key, state) {
|
|
5754
|
+
var _stateOf;
|
|
5755
|
+
if (((_stateOf = stateOf(key)) === null || _stateOf === void 0 ? void 0 : _stateOf.kind) !== "error") setEntry(key, state);
|
|
5756
|
+
};
|
|
5757
|
+
/**
|
|
5758
|
+
* The record's keys as they stand while this patch is walked, for a write
|
|
5759
|
+
* of the whole record to expand against. Only the key set: the source holds
|
|
5760
|
+
* markers, and an entry's content may not be loaded. Seeded from the source
|
|
5761
|
+
* BEFORE this patch and kept current by the whole-entry ops below.
|
|
5762
|
+
*/
|
|
5763
|
+
var keys = null;
|
|
5764
|
+
var currentKeys = function currentKeys() {
|
|
5765
|
+
if (keys === null) {
|
|
5766
|
+
keys = new Map();
|
|
5767
|
+
if (isObjectNode(module.source)) {
|
|
5768
|
+
for (var _i = 0, _Object$keys = Object.keys(module.source); _i < _Object$keys.length; _i++) {
|
|
5769
|
+
var _key = _Object$keys[_i];
|
|
5770
|
+
keys.set(_key, undefined);
|
|
5771
|
+
}
|
|
5772
|
+
}
|
|
5773
|
+
}
|
|
5774
|
+
return keys;
|
|
5775
|
+
};
|
|
5776
|
+
/** Where a file's `patch_id` lands in the source; last write wins its place. */
|
|
5777
|
+
var fileMarks = new Map();
|
|
5778
|
+
/** And in an entry's content. */
|
|
5779
|
+
var entryFileMarks = [];
|
|
5780
|
+
var _iterator = createForOfIteratorHelper._createForOfIteratorHelper(record.patch),
|
|
5781
|
+
_step;
|
|
5782
|
+
try {
|
|
5783
|
+
for (_iterator.s(); !(_step = _iterator.n()).done;) {
|
|
5784
|
+
var rawOp = _step.value;
|
|
5785
|
+
/*
|
|
5786
|
+
* A write of the WHOLE record fans out into per-entry ops first, through
|
|
5787
|
+
* the expansion the commit flow uses too (`expandJsonValuesRootOp`).
|
|
5788
|
+
* Left as it is, it would put content where the source holds markers.
|
|
5789
|
+
*/
|
|
5790
|
+
var ops = [rawOp];
|
|
5791
|
+
if (schema !== undefined && isJsonValuesRootOp(schema, rawOp)) {
|
|
5792
|
+
var expanded = expandJsonValuesRootOp(rawOp, currentKeys());
|
|
5793
|
+
if (fp.result.isErr(expanded)) return fail(expanded.error.message);
|
|
5794
|
+
ops = expanded.value;
|
|
5795
|
+
}
|
|
5796
|
+
var _iterator3 = createForOfIteratorHelper._createForOfIteratorHelper(ops),
|
|
5797
|
+
_step3;
|
|
5798
|
+
try {
|
|
5799
|
+
for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
|
|
5800
|
+
var _op = _step3.value;
|
|
5801
|
+
var cls = schema === undefined ? {
|
|
5802
|
+
kind: "normal"
|
|
5803
|
+
} : classifyJsonValuesOp(schema, _op.path);
|
|
5804
|
+
if (_op.op === "file") {
|
|
5805
|
+
var _op$nestedFilePath;
|
|
5806
|
+
/*
|
|
5807
|
+
* A file op carries bytes, not content. What the content needs is
|
|
5808
|
+
* the `patch_id` saying those bytes are not published yet, so the
|
|
5809
|
+
* file is served from the patch rather than from a `/public` path
|
|
5810
|
+
* that holds nothing. Keyed by where it LANDS -- the field's path
|
|
5811
|
+
* plus `nestedFilePath` -- because a video's poster and captions are
|
|
5812
|
+
* file ops on the video's own path. A delete has no bytes to point at.
|
|
5813
|
+
*/
|
|
5814
|
+
if (_op.value === null) continue;
|
|
5815
|
+
var nested = (_op$nestedFilePath = _op.nestedFilePath) !== null && _op$nestedFilePath !== void 0 ? _op$nestedFilePath : [];
|
|
5816
|
+
if (cls.kind === "entry" && cls.subPath.length > 0) {
|
|
5817
|
+
if (cls.recordPath.length === 0) {
|
|
5818
|
+
entryFileMarks.push({
|
|
5819
|
+
key: cls.entryKey,
|
|
5820
|
+
path: cls.subPath.concat(nested, "patch_id")
|
|
5821
|
+
});
|
|
5822
|
+
}
|
|
5823
|
+
continue;
|
|
5824
|
+
}
|
|
5825
|
+
var at = _op.path.concat(nested, "patch_id");
|
|
5826
|
+
fileMarks.set(JSON.stringify(at), at);
|
|
5827
|
+
if (cls.kind === "entry" && cls.recordPath.length === 0) {
|
|
5828
|
+
// The entry IS the media: its content gets the mark too.
|
|
5829
|
+
entryFileMarks.push({
|
|
5830
|
+
key: cls.entryKey,
|
|
5831
|
+
path: nested.concat("patch_id")
|
|
5832
|
+
});
|
|
5833
|
+
}
|
|
5834
|
+
continue;
|
|
5835
|
+
}
|
|
5836
|
+
if (cls.kind === "normal") {
|
|
5837
|
+
var _applied = applyOp(source, _op);
|
|
5838
|
+
if (fp.result.isErr(_applied)) return fail(_applied.error.message);
|
|
5839
|
+
source = _applied.value;
|
|
5840
|
+
if (_op.op === "move") {
|
|
5841
|
+
var fromKey = wholeEntryKey(schema, _op.from);
|
|
5842
|
+
if (fromKey !== undefined) settle(fromKey, {
|
|
5843
|
+
kind: "deleted"
|
|
5844
|
+
});
|
|
5845
|
+
}
|
|
5846
|
+
if (_op.path.length === 0 && (schema === null || schema === void 0 ? void 0 : schema.type) === "record" && schema.jsonValues === true) {
|
|
5847
|
+
// The record itself was replaced (by something that is not a
|
|
5848
|
+
// record): every entry went with it.
|
|
5849
|
+
clonedBytes += SLOT_BYTES * (entries !== null && entries !== void 0 ? entries : module.entries).size;
|
|
5850
|
+
entries = new Map();
|
|
5851
|
+
}
|
|
5852
|
+
continue;
|
|
5853
|
+
}
|
|
5854
|
+
if (cls.recordPath.length > 0) {
|
|
5855
|
+
return fail("Nested .jsonValues() records are not supported: '".concat(cls.recordPath.join("."), "'. Use .jsonValues() only on a module's root record/router."));
|
|
5856
|
+
}
|
|
5857
|
+
var _key3 = cls.entryKey;
|
|
5858
|
+
if (cls.subPath.length > 0) {
|
|
5859
|
+
// An edit INSIDE an entry: its content, never the source's marker.
|
|
5860
|
+
if (_op.op === "move" || _op.op === "copy") {
|
|
5861
|
+
var fromCls = schema === undefined ? {
|
|
5862
|
+
kind: "normal"
|
|
5863
|
+
} : classifyJsonValuesOp(schema, _op.from);
|
|
5864
|
+
if (fromCls.kind !== "entry" || fromCls.entryKey !== _key3) {
|
|
5865
|
+
return fail("Cannot '".concat(_op.op, "' between different jsonValues entries"));
|
|
5866
|
+
}
|
|
5867
|
+
}
|
|
5868
|
+
var rebased = rebaseContentOp(_op, cls.recordPath.length + 1);
|
|
5869
|
+
if (fp.result.isErr(rebased)) return fail(rebased.error.message);
|
|
5870
|
+
var _state = stateOf(_key3);
|
|
5871
|
+
if (_state === undefined || _state.kind === "deleted") {
|
|
5872
|
+
setEntry(_key3, {
|
|
5873
|
+
kind: "error",
|
|
5874
|
+
patchId: patchId,
|
|
5875
|
+
message: "Cannot edit jsonValues entry '".concat(_key3, "': it does not exist")
|
|
5876
|
+
});
|
|
5877
|
+
} else if (_state.kind === "waiting") {
|
|
5878
|
+
setEntry(_key3, {
|
|
5879
|
+
kind: "waiting",
|
|
5880
|
+
from: _state.from,
|
|
5881
|
+
ops: {
|
|
5882
|
+
op: rebased.value,
|
|
5883
|
+
patchId: patchId,
|
|
5884
|
+
before: _state.ops
|
|
5885
|
+
}
|
|
5886
|
+
});
|
|
5887
|
+
} else if (_state.kind === "content") {
|
|
5888
|
+
var _applied2 = applyOp(_state.content, rebased.value);
|
|
5889
|
+
setEntry(_key3, fp.result.isErr(_applied2) ? {
|
|
5890
|
+
kind: "error",
|
|
5891
|
+
patchId: patchId,
|
|
5892
|
+
message: _applied2.error.message
|
|
5893
|
+
} : {
|
|
5894
|
+
kind: "content",
|
|
5895
|
+
content: _applied2.value
|
|
5896
|
+
});
|
|
5897
|
+
}
|
|
5898
|
+
continue;
|
|
5899
|
+
}
|
|
5900
|
+
|
|
5901
|
+
// A WHOLE entry: a marker in the source, content in the entry.
|
|
5902
|
+
var marker = createForOfIteratorHelper._defineProperty(createForOfIteratorHelper._defineProperty({}, core.VAL_EXTENSION, "json"), "patch_id", patchId);
|
|
5903
|
+
if (_op.op === "add" || _op.op === "replace") {
|
|
5904
|
+
var written = _op.op === "add" ? addAt(source, _op.path, marker) : replaceAt(source, _op.path, marker);
|
|
5905
|
+
if (fp.result.isErr(written)) return fail(written.error.message);
|
|
5906
|
+
source = written.value;
|
|
5907
|
+
currentKeys().set(_key3, undefined);
|
|
5908
|
+
settle(_key3, {
|
|
5909
|
+
kind: "content",
|
|
5910
|
+
content: _op.value
|
|
5911
|
+
});
|
|
5912
|
+
} else if (_op.op === "remove") {
|
|
5913
|
+
var removed = removeAt(source, _op.path);
|
|
5914
|
+
if (fp.result.isErr(removed)) return fail(removed.error.message);
|
|
5915
|
+
source = removed.value.doc;
|
|
5916
|
+
currentKeys()["delete"](_key3);
|
|
5917
|
+
settle(_key3, {
|
|
5918
|
+
kind: "deleted"
|
|
5919
|
+
});
|
|
5920
|
+
} else if (_op.op === "move" || _op.op === "copy") {
|
|
5921
|
+
var _fromCls = schema === undefined ? {
|
|
5922
|
+
kind: "normal"
|
|
5923
|
+
} : classifyJsonValuesOp(schema, _op.from);
|
|
5924
|
+
if (_fromCls.kind !== "entry" || _fromCls.subPath.length !== 0 || _fromCls.recordPath.length !== 0) {
|
|
5925
|
+
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("."), "')"));
|
|
5926
|
+
}
|
|
5927
|
+
var _fromKey = _fromCls.entryKey;
|
|
5928
|
+
// Read before anything moves: the destination is the source entry
|
|
5929
|
+
// as it stood at this op.
|
|
5930
|
+
var fromState = stateOf(_fromKey);
|
|
5931
|
+
var added = addAt(source, _op.path, marker);
|
|
5932
|
+
if (fp.result.isErr(added)) return fail(added.error.message);
|
|
5933
|
+
source = added.value;
|
|
5934
|
+
currentKeys().set(_key3, undefined);
|
|
5935
|
+
if (_op.op === "move") {
|
|
5936
|
+
var _removed = removeAt(source, _op.from);
|
|
5937
|
+
if (fp.result.isErr(_removed)) return fail(_removed.error.message);
|
|
5938
|
+
source = _removed.value.doc;
|
|
5939
|
+
currentKeys()["delete"](_fromKey);
|
|
5940
|
+
}
|
|
5941
|
+
settle(_key3, fromState === undefined || fromState.kind === "deleted" ? {
|
|
5942
|
+
kind: "error",
|
|
5943
|
+
patchId: patchId,
|
|
5944
|
+
message: "Cannot resolve '".concat(_op.op, "' of jsonValues entry '").concat(_key3, "' from its own content")
|
|
5945
|
+
} : fromState);
|
|
5946
|
+
if (_op.op === "move" && _fromKey !== _key3) {
|
|
5947
|
+
settle(_fromKey, {
|
|
5948
|
+
kind: "deleted"
|
|
5949
|
+
});
|
|
5950
|
+
}
|
|
5951
|
+
} else {
|
|
5952
|
+
// `test` of a whole entry: the commit refuses it (`ValOps.prepare`),
|
|
5953
|
+
// so the read does too, rather than showing a patch publishing fails.
|
|
5954
|
+
return fail("Unsupported op '".concat(_op.op, "' on jsonValues entry '").concat(_key3, "' (supported: add, remove, replace, move, copy)"));
|
|
5955
|
+
}
|
|
5956
|
+
}
|
|
5957
|
+
} catch (err) {
|
|
5958
|
+
_iterator3.e(err);
|
|
5959
|
+
} finally {
|
|
5960
|
+
_iterator3.f();
|
|
5961
|
+
}
|
|
5962
|
+
}
|
|
5963
|
+
} catch (err) {
|
|
5964
|
+
_iterator.e(err);
|
|
5965
|
+
} finally {
|
|
5966
|
+
_iterator.f();
|
|
5967
|
+
}
|
|
5968
|
+
var _iterator2 = createForOfIteratorHelper._createForOfIteratorHelper(fileMarks.values()),
|
|
5969
|
+
_step2;
|
|
5970
|
+
try {
|
|
5971
|
+
for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
|
|
5972
|
+
var _at = _step2.value;
|
|
5973
|
+
var marked = addAt(source, _at, patchId);
|
|
5974
|
+
if (fp.result.isErr(marked)) return fail(marked.error.message);
|
|
5975
|
+
source = marked.value;
|
|
5976
|
+
}
|
|
5977
|
+
} catch (err) {
|
|
5978
|
+
_iterator2.e(err);
|
|
5979
|
+
} finally {
|
|
5980
|
+
_iterator2.f();
|
|
5981
|
+
}
|
|
5982
|
+
for (var _i2 = 0, _entryFileMarks = entryFileMarks; _i2 < _entryFileMarks.length; _i2++) {
|
|
5983
|
+
var _entryFileMarks$_i = _entryFileMarks[_i2],
|
|
5984
|
+
_key2 = _entryFileMarks$_i.key,
|
|
5985
|
+
path = _entryFileMarks$_i.path;
|
|
5986
|
+
var state = stateOf(_key2);
|
|
5987
|
+
var op = {
|
|
5988
|
+
op: "add",
|
|
5989
|
+
path: path,
|
|
5990
|
+
value: patchId
|
|
5991
|
+
};
|
|
5992
|
+
if ((state === null || state === void 0 ? void 0 : state.kind) === "content") {
|
|
5993
|
+
var applied = applyOp(state.content, op);
|
|
5994
|
+
setEntry(_key2, fp.result.isErr(applied) ? {
|
|
5995
|
+
kind: "error",
|
|
5996
|
+
patchId: patchId,
|
|
5997
|
+
message: applied.error.message
|
|
5998
|
+
} : {
|
|
5999
|
+
kind: "content",
|
|
6000
|
+
content: applied.value
|
|
6001
|
+
});
|
|
6002
|
+
} else if ((state === null || state === void 0 ? void 0 : state.kind) === "waiting") {
|
|
6003
|
+
setEntry(_key2, {
|
|
6004
|
+
kind: "waiting",
|
|
6005
|
+
from: state.from,
|
|
6006
|
+
ops: {
|
|
6007
|
+
op: op,
|
|
6008
|
+
patchId: patchId,
|
|
6009
|
+
before: state.ops
|
|
6010
|
+
}
|
|
6011
|
+
});
|
|
6012
|
+
}
|
|
6013
|
+
}
|
|
6014
|
+
return {
|
|
6015
|
+
status: "applied",
|
|
6016
|
+
module: {
|
|
6017
|
+
source: source,
|
|
6018
|
+
entries: entries !== null && entries !== void 0 ? entries : module.entries
|
|
6019
|
+
}
|
|
6020
|
+
};
|
|
6021
|
+
}
|
|
6022
|
+
|
|
6023
|
+
/**
|
|
6024
|
+
* A module's chain: every patch in order, stopping at the first that does
|
|
6025
|
+
* not apply. `applied` is how many did, so the chain the content is AT is
|
|
6026
|
+
* the first `applied` ids -- what its version names.
|
|
6027
|
+
*/
|
|
6028
|
+
function applyChain(module, records, schema) {
|
|
6029
|
+
var current = module;
|
|
6030
|
+
var _iterator4 = createForOfIteratorHelper._createForOfIteratorHelper(records.entries()),
|
|
6031
|
+
_step4;
|
|
6032
|
+
try {
|
|
6033
|
+
for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) {
|
|
6034
|
+
var _step4$value = createForOfIteratorHelper._slicedToArray(_step4.value, 2),
|
|
6035
|
+
index = _step4$value[0],
|
|
6036
|
+
record = _step4$value[1];
|
|
6037
|
+
var res = applyPatchRecord(current, record, schema);
|
|
6038
|
+
if (res.status === "failed") {
|
|
6039
|
+
return {
|
|
6040
|
+
module: current,
|
|
6041
|
+
applied: index,
|
|
6042
|
+
failed: {
|
|
6043
|
+
patchId: res.patchId,
|
|
6044
|
+
message: res.message
|
|
6045
|
+
}
|
|
6046
|
+
};
|
|
6047
|
+
}
|
|
6048
|
+
current = res.module;
|
|
6049
|
+
}
|
|
6050
|
+
} catch (err) {
|
|
6051
|
+
_iterator4.e(err);
|
|
6052
|
+
} finally {
|
|
6053
|
+
_iterator4.f();
|
|
6054
|
+
}
|
|
6055
|
+
return {
|
|
6056
|
+
module: current,
|
|
6057
|
+
applied: records.length,
|
|
6058
|
+
failed: null
|
|
6059
|
+
};
|
|
6060
|
+
}
|
|
6061
|
+
|
|
6062
|
+
// #region entries
|
|
6063
|
+
|
|
6064
|
+
/**
|
|
6065
|
+
* What an entry is in this module: what the chain made it, or -- for one the
|
|
6066
|
+
* chain never touched -- the base's, if the source has its marker.
|
|
6067
|
+
*/
|
|
6068
|
+
function entryState(module, key) {
|
|
6069
|
+
var _module$entries$get;
|
|
6070
|
+
return (_module$entries$get = module.entries.get(key)) !== null && _module$entries$get !== void 0 ? _module$entries$get : implicitState(module.source, key);
|
|
6071
|
+
}
|
|
6072
|
+
function implicitState(source, key) {
|
|
6073
|
+
return isObjectNode(source) && Object.prototype.hasOwnProperty.call(source, key) && core.Internal.isJson(source[key]) ? {
|
|
6074
|
+
kind: "waiting",
|
|
6075
|
+
from: key,
|
|
6076
|
+
ops: null
|
|
6077
|
+
} : undefined;
|
|
6078
|
+
}
|
|
6079
|
+
|
|
6080
|
+
/** An entry's content, read. */
|
|
6081
|
+
|
|
6082
|
+
/**
|
|
6083
|
+
* An entry's content: what {@link entryState} says, with what it was waiting
|
|
6084
|
+
* for applied. `baseContentOf` is the BASE content of an entry by key (its
|
|
6085
|
+
* `*.val.json`, or what a save left there), `undefined` for one the base does
|
|
6086
|
+
* not have.
|
|
6087
|
+
*/
|
|
6088
|
+
function resolveEntry(key, state, baseContentOf) {
|
|
6089
|
+
if (state === undefined || state.kind === "deleted") {
|
|
6090
|
+
return {
|
|
6091
|
+
kind: "missing"
|
|
6092
|
+
};
|
|
6093
|
+
}
|
|
6094
|
+
if (state.kind === "error") {
|
|
6095
|
+
return {
|
|
6096
|
+
kind: "error",
|
|
6097
|
+
message: state.message,
|
|
6098
|
+
patchId: state.patchId
|
|
6099
|
+
};
|
|
6100
|
+
}
|
|
6101
|
+
if (state.kind === "content") {
|
|
6102
|
+
return {
|
|
6103
|
+
kind: "content",
|
|
6104
|
+
content: state.content
|
|
6105
|
+
};
|
|
6106
|
+
}
|
|
6107
|
+
var held = [];
|
|
6108
|
+
for (var at = state.ops; at !== null; at = at.before) held.push(at);
|
|
6109
|
+
var content = baseContentOf(state.from);
|
|
6110
|
+
if (content === undefined) {
|
|
6111
|
+
var first = held[held.length - 1];
|
|
6112
|
+
return first === undefined ? {
|
|
6113
|
+
kind: "missing"
|
|
6114
|
+
} : {
|
|
6115
|
+
kind: "error",
|
|
6116
|
+
message: "Cannot edit jsonValues entry '".concat(key, "': it does not exist"),
|
|
6117
|
+
patchId: first.patchId
|
|
6118
|
+
};
|
|
6119
|
+
}
|
|
6120
|
+
for (var i = held.length - 1; i >= 0; i--) {
|
|
6121
|
+
var _held$i = held[i],
|
|
6122
|
+
op = _held$i.op,
|
|
6123
|
+
patchId = _held$i.patchId;
|
|
6124
|
+
var applied = applyOp(content, op);
|
|
6125
|
+
if (fp.result.isErr(applied)) {
|
|
6126
|
+
return {
|
|
6127
|
+
kind: "error",
|
|
6128
|
+
message: applied.error.message,
|
|
6129
|
+
patchId: patchId
|
|
6130
|
+
};
|
|
6131
|
+
}
|
|
6132
|
+
content = applied.value;
|
|
6133
|
+
}
|
|
6134
|
+
return {
|
|
6135
|
+
kind: "content",
|
|
6136
|
+
content: content
|
|
6137
|
+
};
|
|
6138
|
+
}
|
|
6139
|
+
|
|
6140
|
+
// #region copy-on-write JSON ops
|
|
6141
|
+
|
|
6142
|
+
/*
|
|
6143
|
+
* RFC 6902 as `JSONOps` (in `@valbuild/core/patch`) applies it -- the same
|
|
6144
|
+
* results and the same error messages, which the conformance cases hold --
|
|
6145
|
+
* but without mutating anything: a container on an op's path is copied, and
|
|
6146
|
+
* everything else is shared.
|
|
6147
|
+
*/
|
|
6148
|
+
|
|
6149
|
+
function isArrayNode(node) {
|
|
6150
|
+
return Array.isArray(node);
|
|
6151
|
+
}
|
|
6152
|
+
function isObjectNode(node) {
|
|
6153
|
+
return createForOfIteratorHelper._typeof(node) === "object" && node !== null && !Array.isArray(node);
|
|
6154
|
+
}
|
|
6155
|
+
function hasOwn(node, key) {
|
|
6156
|
+
return Object.prototype.hasOwnProperty.call(node, key);
|
|
6157
|
+
}
|
|
6158
|
+
function patchError(message) {
|
|
6159
|
+
return fp.result.err(new patch.PatchError(message));
|
|
6160
|
+
}
|
|
6161
|
+
function copyArray(node) {
|
|
6162
|
+
clonedBytes += SLOT_BYTES * node.length;
|
|
6163
|
+
return node.slice();
|
|
6164
|
+
}
|
|
6165
|
+
|
|
6166
|
+
/**
|
|
6167
|
+
* A shallow copy, key by key. Not a spread, which was tried: a module's root
|
|
6168
|
+
* is a large object in dictionary mode, and V8 spreads one of forty thousand
|
|
6169
|
+
* keys at about half this speed (the 5 MB keystroke of
|
|
6170
|
+
* `scenarios/measure/keystroke.ts` went from 19 ms to 33).
|
|
6171
|
+
*/
|
|
6172
|
+
function copyObject(node) {
|
|
6173
|
+
var copy = {};
|
|
6174
|
+
var slots = 0;
|
|
6175
|
+
for (var _i3 = 0, _Object$keys2 = Object.keys(node); _i3 < _Object$keys2.length; _i3++) {
|
|
6176
|
+
var _key4 = _Object$keys2[_i3];
|
|
6177
|
+
setOwn(copy, _key4, node[_key4]);
|
|
6178
|
+
slots++;
|
|
6179
|
+
}
|
|
6180
|
+
clonedBytes += SLOT_BYTES * slots;
|
|
6181
|
+
return copy;
|
|
6182
|
+
}
|
|
6183
|
+
|
|
6184
|
+
/** An own property, even for `__proto__`, which `=` would not create. */
|
|
6185
|
+
function setOwn(target, key, value) {
|
|
6186
|
+
if (key === "__proto__") {
|
|
6187
|
+
Object.defineProperty(target, key, {
|
|
6188
|
+
value: value,
|
|
6189
|
+
writable: true,
|
|
6190
|
+
enumerable: true,
|
|
6191
|
+
configurable: true
|
|
6192
|
+
});
|
|
6193
|
+
} else {
|
|
6194
|
+
target[key] = value;
|
|
6195
|
+
}
|
|
6196
|
+
}
|
|
6197
|
+
function arrayIndex(key, node, inclusive) {
|
|
6198
|
+
if (inclusive && key === "-") return fp.result.ok(node.length);
|
|
6199
|
+
var index = patch.parseAndValidateArrayIndex(key);
|
|
6200
|
+
if (fp.result.isErr(index)) return index;
|
|
6201
|
+
return (inclusive ? index.value <= node.length : index.value < node.length) ? index : patchError("Array index out of bounds");
|
|
6202
|
+
}
|
|
6203
|
+
function childOf(node, key) {
|
|
6204
|
+
if (isArrayNode(node)) {
|
|
6205
|
+
var index = patch.parseAndValidateArrayIndex(key);
|
|
6206
|
+
if (fp.result.isErr(index)) return index;
|
|
6207
|
+
return index.value >= node.length ? patchError("Array index out of bounds") : fp.result.ok(node[index.value]);
|
|
6208
|
+
}
|
|
6209
|
+
if (isObjectNode(node)) {
|
|
6210
|
+
return fp.result.ok(hasOwn(node, key) ? node[key] : undefined);
|
|
6211
|
+
}
|
|
6212
|
+
return patchError("Cannot access non-object/array");
|
|
6213
|
+
}
|
|
6214
|
+
|
|
6215
|
+
/** `node` with `key` set to `value`: the one copy a step on a path costs. */
|
|
6216
|
+
function withChild(node, key, value) {
|
|
6217
|
+
if (isArrayNode(node)) {
|
|
6218
|
+
var copy = copyArray(node);
|
|
6219
|
+
copy[Number(key)] = value;
|
|
6220
|
+
return copy;
|
|
6221
|
+
}
|
|
6222
|
+
if (isObjectNode(node)) {
|
|
6223
|
+
var _copy = copyObject(node);
|
|
6224
|
+
setOwn(_copy, key, value);
|
|
6225
|
+
return _copy;
|
|
6226
|
+
}
|
|
6227
|
+
return node;
|
|
6228
|
+
}
|
|
6229
|
+
|
|
6230
|
+
/**
|
|
6231
|
+
* Walks `path` to the parent of its last segment, lets `leaf` change that
|
|
6232
|
+
* parent, and copies every container on the way back up. Errors as
|
|
6233
|
+
* `JSONOps`'s pointer walk: the first missing step is reported.
|
|
6234
|
+
*/
|
|
6235
|
+
function updateAt(node, path, index, leaf) {
|
|
6236
|
+
var key = path[index];
|
|
6237
|
+
if (index === path.length - 1) return leaf(node, key);
|
|
6238
|
+
var child = childOf(node, key);
|
|
6239
|
+
if (fp.result.isErr(child)) return child;
|
|
6240
|
+
if (child.value === undefined) {
|
|
6241
|
+
return patchError("Path refers to non-existing object/array");
|
|
6242
|
+
}
|
|
6243
|
+
var updated = updateAt(child.value, path, index + 1, leaf);
|
|
6244
|
+
if (fp.result.isErr(updated)) return updated;
|
|
6245
|
+
return fp.result.ok({
|
|
6246
|
+
node: updated.value.node === child.value ? node : withChild(node, key, updated.value.node),
|
|
6247
|
+
out: updated.value.out
|
|
6248
|
+
});
|
|
6249
|
+
}
|
|
6250
|
+
function isNonEmpty(path) {
|
|
6251
|
+
return path.length > 0;
|
|
6252
|
+
}
|
|
6253
|
+
function getAt(doc, path) {
|
|
6254
|
+
var node = doc;
|
|
6255
|
+
var _iterator5 = createForOfIteratorHelper._createForOfIteratorHelper(path),
|
|
6256
|
+
_step5;
|
|
6257
|
+
try {
|
|
6258
|
+
for (_iterator5.s(); !(_step5 = _iterator5.n()).done;) {
|
|
6259
|
+
var _key5 = _step5.value;
|
|
6260
|
+
var child = childOf(node, _key5);
|
|
6261
|
+
if (fp.result.isErr(child)) return child;
|
|
6262
|
+
if (child.value === undefined) {
|
|
6263
|
+
return patchError("Path refers to non-existing object/array");
|
|
6264
|
+
}
|
|
6265
|
+
node = child.value;
|
|
6266
|
+
}
|
|
6267
|
+
} catch (err) {
|
|
6268
|
+
_iterator5.e(err);
|
|
6269
|
+
} finally {
|
|
6270
|
+
_iterator5.f();
|
|
6271
|
+
}
|
|
6272
|
+
return fp.result.ok(node);
|
|
6273
|
+
}
|
|
6274
|
+
function addAt(doc, path, value) {
|
|
6275
|
+
if (!isNonEmpty(path)) return fp.result.ok(value);
|
|
6276
|
+
var res = updateAt(doc, path, 0, function (parent, key) {
|
|
6277
|
+
if (isArrayNode(parent)) {
|
|
6278
|
+
var index = arrayIndex(key, parent, true);
|
|
6279
|
+
if (fp.result.isErr(index)) return index;
|
|
6280
|
+
var copy = copyArray(parent);
|
|
6281
|
+
copy.splice(index.value, 0, value);
|
|
6282
|
+
return fp.result.ok({
|
|
6283
|
+
node: copy,
|
|
6284
|
+
out: null
|
|
6285
|
+
});
|
|
6286
|
+
}
|
|
6287
|
+
if (isObjectNode(parent)) {
|
|
6288
|
+
var _copy2 = copyObject(parent);
|
|
6289
|
+
setOwn(_copy2, key, value);
|
|
6290
|
+
return fp.result.ok({
|
|
6291
|
+
node: _copy2,
|
|
6292
|
+
out: null
|
|
6293
|
+
});
|
|
6294
|
+
}
|
|
6295
|
+
return patchError("Cannot add to non-object/array");
|
|
6296
|
+
});
|
|
6297
|
+
return fp.result.isErr(res) ? res : fp.result.ok(res.value.node);
|
|
6298
|
+
}
|
|
6299
|
+
function replaceAt(doc, path, value) {
|
|
6300
|
+
if (!isNonEmpty(path)) return fp.result.ok(value);
|
|
6301
|
+
var res = updateAt(doc, path, 0, function (parent, key) {
|
|
6302
|
+
if (isArrayNode(parent)) {
|
|
6303
|
+
var index = arrayIndex(key, parent, false);
|
|
6304
|
+
if (fp.result.isErr(index)) return index;
|
|
6305
|
+
var copy = copyArray(parent);
|
|
6306
|
+
copy[index.value] = value;
|
|
6307
|
+
return fp.result.ok({
|
|
6308
|
+
node: copy,
|
|
6309
|
+
out: null
|
|
6310
|
+
});
|
|
6311
|
+
}
|
|
6312
|
+
if (isObjectNode(parent)) {
|
|
6313
|
+
if (!hasOwn(parent, key)) {
|
|
6314
|
+
return patchError("Cannot replace object element which does not exist");
|
|
6315
|
+
}
|
|
6316
|
+
var _copy3 = copyObject(parent);
|
|
6317
|
+
setOwn(_copy3, key, value);
|
|
6318
|
+
return fp.result.ok({
|
|
6319
|
+
node: _copy3,
|
|
6320
|
+
out: null
|
|
6321
|
+
});
|
|
6322
|
+
}
|
|
6323
|
+
return patchError("Cannot replace in non-object/array");
|
|
6324
|
+
});
|
|
6325
|
+
return fp.result.isErr(res) ? res : fp.result.ok(res.value.node);
|
|
6326
|
+
}
|
|
6327
|
+
function removeAt(doc, path) {
|
|
6328
|
+
if (!isNonEmpty(path)) {
|
|
6329
|
+
return patchError("Cannot remove from non-object/array");
|
|
6330
|
+
}
|
|
6331
|
+
var res = updateAt(doc, path, 0, function (parent, key) {
|
|
6332
|
+
if (isArrayNode(parent)) {
|
|
6333
|
+
var index = arrayIndex(key, parent, false);
|
|
6334
|
+
if (fp.result.isErr(index)) return index;
|
|
6335
|
+
var copy = copyArray(parent);
|
|
6336
|
+
var _copy$splice = copy.splice(index.value, 1),
|
|
6337
|
+
_copy$splice2 = createForOfIteratorHelper._slicedToArray(_copy$splice, 1),
|
|
6338
|
+
removed = _copy$splice2[0];
|
|
6339
|
+
return fp.result.ok({
|
|
6340
|
+
node: copy,
|
|
6341
|
+
out: removed
|
|
6342
|
+
});
|
|
6343
|
+
}
|
|
6344
|
+
if (isObjectNode(parent) && hasOwn(parent, key)) {
|
|
6345
|
+
var _copy4 = copyObject(parent);
|
|
6346
|
+
var _removed2 = parent[key];
|
|
6347
|
+
delete _copy4[key];
|
|
6348
|
+
return fp.result.ok({
|
|
6349
|
+
node: _copy4,
|
|
6350
|
+
out: _removed2
|
|
6351
|
+
});
|
|
6352
|
+
}
|
|
6353
|
+
return patchError("Cannot remove from non-object/array");
|
|
6354
|
+
});
|
|
6355
|
+
return fp.result.isErr(res) ? res : fp.result.ok({
|
|
6356
|
+
doc: res.value.node,
|
|
6357
|
+
removed: res.value.out
|
|
6358
|
+
});
|
|
6359
|
+
}
|
|
6360
|
+
|
|
6361
|
+
/** One op on a document. `file` is not an op on content: the caller marks. */
|
|
6362
|
+
function applyOp(doc, op) {
|
|
6363
|
+
switch (op.op) {
|
|
6364
|
+
case "add":
|
|
6365
|
+
return addAt(doc, op.path, op.value);
|
|
6366
|
+
case "replace":
|
|
6367
|
+
return replaceAt(doc, op.path, op.value);
|
|
6368
|
+
case "remove":
|
|
6369
|
+
{
|
|
6370
|
+
var removed = removeAt(doc, op.path);
|
|
6371
|
+
return fp.result.isErr(removed) ? removed : fp.result.ok(removed.value.doc);
|
|
6372
|
+
}
|
|
6373
|
+
case "move":
|
|
6374
|
+
{
|
|
6375
|
+
var _removed3 = removeAt(doc, op.from);
|
|
6376
|
+
if (fp.result.isErr(_removed3)) return _removed3;
|
|
6377
|
+
return addAt(_removed3.value.doc, op.path, _removed3.value.removed);
|
|
6378
|
+
}
|
|
6379
|
+
case "copy":
|
|
6380
|
+
{
|
|
6381
|
+
// Shared, not cloned: nothing here mutates, so two places can hold one
|
|
6382
|
+
// value.
|
|
6383
|
+
var value = getAt(doc, op.from);
|
|
6384
|
+
if (fp.result.isErr(value)) return value;
|
|
6385
|
+
return addAt(doc, op.path, value.value);
|
|
6386
|
+
}
|
|
6387
|
+
case "test":
|
|
6388
|
+
{
|
|
6389
|
+
// As `applyPatch` in `@valbuild/core/patch`, which the commit applies
|
|
6390
|
+
// `.val.ts` text through: a path that does not exist, or a value there
|
|
6391
|
+
// that is not deeply equal, fails the patch (RFC 6902, 4.6). The two
|
|
6392
|
+
// have to agree, or a draft shows a patch Publish refuses.
|
|
6393
|
+
var _value = getAt(doc, op.path);
|
|
6394
|
+
if (fp.result.isErr(_value)) return _value;
|
|
6395
|
+
if (!patch.deepEqual(op.value, _value.value)) return patchError("Test failed");
|
|
6396
|
+
return fp.result.ok(doc);
|
|
6397
|
+
}
|
|
6398
|
+
case "file":
|
|
6399
|
+
return patchError("Cannot apply a file patch here");
|
|
6400
|
+
}
|
|
6401
|
+
}
|
|
6402
|
+
|
|
6403
|
+
/*
|
|
6404
|
+
* The store: modules by address. See `docs/plans/one-content-model.md`,
|
|
6405
|
+
* "The store: one address, immutable values, tiers".
|
|
6406
|
+
*
|
|
6407
|
+
* Content at a version never changes, so a store of modules by `moduleKey`
|
|
6408
|
+
* needs no invalidation, only eviction. A base is a version with the empty
|
|
6409
|
+
* chain, so the same store holds both. Every place that keeps modules uses
|
|
6410
|
+
* the same tiers, in the order that fits it: Val's server keeps
|
|
6411
|
+
* `[memoryTier, bundleTier, …]` in front of its head (`ValHead` in
|
|
6412
|
+
* `@valbuild/server`), and the Studio's will put an origin tier behind them
|
|
6413
|
+
* (phase 7).
|
|
6414
|
+
*/
|
|
6415
|
+
|
|
6416
|
+
/** One module's content at one version, as a tier holds it. */
|
|
6417
|
+
|
|
6418
|
+
// #region counted work
|
|
6419
|
+
|
|
6420
|
+
var tierHits = new Map();
|
|
6421
|
+
var tierMisses = 0;
|
|
6422
|
+
|
|
6423
|
+
/**
|
|
6424
|
+
* What the stores in this process have answered, by tier, and how often
|
|
6425
|
+
* none of them had a key: a test takes it before and after. Never reset.
|
|
6426
|
+
*/
|
|
6427
|
+
function storeWork() {
|
|
6428
|
+
return {
|
|
6429
|
+
hits: Object.fromEntries(tierHits),
|
|
6430
|
+
misses: tierMisses
|
|
6431
|
+
};
|
|
6432
|
+
}
|
|
6433
|
+
|
|
6434
|
+
// #region tiered
|
|
6435
|
+
|
|
6436
|
+
/**
|
|
6437
|
+
* Asks each tier in turn; what a slower tier answers is written to the faster
|
|
6438
|
+
* ones before it. A tier that answers `null` has no say.
|
|
6439
|
+
*/
|
|
6440
|
+
function tiered(tiers) {
|
|
6441
|
+
return {
|
|
6442
|
+
name: tiers.map(function (tier) {
|
|
6443
|
+
return tier.name;
|
|
6444
|
+
}).join(">"),
|
|
6445
|
+
get: function get(key) {
|
|
6446
|
+
return createForOfIteratorHelper._asyncToGenerator(/*#__PURE__*/createForOfIteratorHelper._regenerator().m(function _callee() {
|
|
6447
|
+
var _iterator, _step, _tierHits$get, _step$value, index, tier, found, _iterator2, _step2, _faster$put, faster, _t;
|
|
6448
|
+
return createForOfIteratorHelper._regenerator().w(function (_context) {
|
|
6449
|
+
while (1) switch (_context.p = _context.n) {
|
|
6450
|
+
case 0:
|
|
6451
|
+
_iterator = createForOfIteratorHelper._createForOfIteratorHelper(tiers.entries());
|
|
6452
|
+
_context.p = 1;
|
|
6453
|
+
_iterator.s();
|
|
6454
|
+
case 2:
|
|
6455
|
+
if ((_step = _iterator.n()).done) {
|
|
6456
|
+
_context.n = 6;
|
|
6457
|
+
break;
|
|
6458
|
+
}
|
|
6459
|
+
_step$value = createForOfIteratorHelper._slicedToArray(_step.value, 2), index = _step$value[0], tier = _step$value[1];
|
|
6460
|
+
_context.n = 3;
|
|
6461
|
+
return tier.get(key);
|
|
6462
|
+
case 3:
|
|
6463
|
+
found = _context.v;
|
|
6464
|
+
if (!(found === null)) {
|
|
6465
|
+
_context.n = 4;
|
|
6466
|
+
break;
|
|
6467
|
+
}
|
|
6468
|
+
return _context.a(3, 5);
|
|
6469
|
+
case 4:
|
|
6470
|
+
tierHits.set(tier.name, ((_tierHits$get = tierHits.get(tier.name)) !== null && _tierHits$get !== void 0 ? _tierHits$get : 0) + 1);
|
|
6471
|
+
_iterator2 = createForOfIteratorHelper._createForOfIteratorHelper(tiers.slice(0, index));
|
|
6472
|
+
try {
|
|
6473
|
+
for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
|
|
6474
|
+
faster = _step2.value;
|
|
6475
|
+
(_faster$put = faster.put) === null || _faster$put === void 0 || _faster$put.call(faster, key, found, found.bytes);
|
|
6476
|
+
}
|
|
6477
|
+
} catch (err) {
|
|
6478
|
+
_iterator2.e(err);
|
|
6479
|
+
} finally {
|
|
6480
|
+
_iterator2.f();
|
|
6481
|
+
}
|
|
6482
|
+
return _context.a(2, found);
|
|
6483
|
+
case 5:
|
|
6484
|
+
_context.n = 2;
|
|
6485
|
+
break;
|
|
6486
|
+
case 6:
|
|
6487
|
+
_context.n = 8;
|
|
6488
|
+
break;
|
|
6489
|
+
case 7:
|
|
6490
|
+
_context.p = 7;
|
|
6491
|
+
_t = _context.v;
|
|
6492
|
+
_iterator.e(_t);
|
|
6493
|
+
case 8:
|
|
6494
|
+
_context.p = 8;
|
|
6495
|
+
_iterator.f();
|
|
6496
|
+
return _context.f(8);
|
|
6497
|
+
case 9:
|
|
6498
|
+
tierMisses++;
|
|
6499
|
+
return _context.a(2, null);
|
|
6500
|
+
}
|
|
6501
|
+
}, _callee, null, [[1, 7, 8, 9]]);
|
|
6502
|
+
}))();
|
|
6503
|
+
},
|
|
6504
|
+
put: function put(key, value, bytes) {
|
|
6505
|
+
var _tiers$, _tiers$$put;
|
|
6506
|
+
(_tiers$ = tiers[0]) === null || _tiers$ === void 0 || (_tiers$$put = _tiers$.put) === null || _tiers$$put === void 0 || _tiers$$put.call(_tiers$, key, value, bytes);
|
|
6507
|
+
}
|
|
6508
|
+
};
|
|
6509
|
+
}
|
|
6510
|
+
|
|
6511
|
+
// #region memory
|
|
6512
|
+
|
|
6513
|
+
/**
|
|
6514
|
+
* Bounded by bytes; keeps the last `lastPerModule` versions per module path
|
|
6515
|
+
* (and, apart from them, the last `lastPerModule` bases), and lets go of the least recently read value when over `budgetBytes`. A
|
|
6516
|
+
* value bigger than the whole budget is still held while it is the newest,
|
|
6517
|
+
* so the read that made it is answered; the next put lets it go.
|
|
6518
|
+
*/
|
|
6519
|
+
function memoryTier(opts) {
|
|
6520
|
+
var _opts$name;
|
|
6521
|
+
/** In least-recently-read order: a read moves a key to the end. */
|
|
6522
|
+
var held = new Map();
|
|
6523
|
+
/** Per module, its keys oldest first. */
|
|
6524
|
+
var rings = new Map();
|
|
6525
|
+
var bytes = 0;
|
|
6526
|
+
var evicted = 0;
|
|
6527
|
+
var versionOf = function versionOf(key) {
|
|
6528
|
+
var _held$get;
|
|
6529
|
+
return (_held$get = held.get(key)) === null || _held$get === void 0 ? void 0 : _held$get.value.version;
|
|
6530
|
+
};
|
|
6531
|
+
var drop = function drop(key) {
|
|
6532
|
+
var entry = held.get(key);
|
|
6533
|
+
if (entry === undefined) return;
|
|
6534
|
+
held["delete"](key);
|
|
6535
|
+
bytes -= entry.bytes;
|
|
6536
|
+
evicted++;
|
|
6537
|
+
var ring = rings.get(entry.value.path);
|
|
6538
|
+
if (ring !== undefined) {
|
|
6539
|
+
var at = ring.indexOf(key);
|
|
6540
|
+
if (at >= 0) ring.splice(at, 1);
|
|
6541
|
+
if (ring.length === 0) rings["delete"](entry.value.path);
|
|
6542
|
+
}
|
|
6543
|
+
};
|
|
6544
|
+
return {
|
|
6545
|
+
name: (_opts$name = opts.name) !== null && _opts$name !== void 0 ? _opts$name : "memory",
|
|
6546
|
+
get: function get(key) {
|
|
6547
|
+
var entry = held.get(key);
|
|
6548
|
+
if (entry === undefined) return null;
|
|
6549
|
+
held["delete"](key);
|
|
6550
|
+
held.set(key, entry);
|
|
6551
|
+
return entry.value;
|
|
6552
|
+
},
|
|
6553
|
+
put: function put(key, value, valueBytes) {
|
|
6554
|
+
var _rings$get;
|
|
6555
|
+
if (held.has(key)) {
|
|
6556
|
+
drop(key);
|
|
6557
|
+
evicted--;
|
|
6558
|
+
}
|
|
6559
|
+
held.set(key, {
|
|
6560
|
+
value: value,
|
|
6561
|
+
bytes: valueBytes
|
|
6562
|
+
});
|
|
6563
|
+
bytes += valueBytes;
|
|
6564
|
+
var ring = (_rings$get = rings.get(value.path)) !== null && _rings$get !== void 0 ? _rings$get : [];
|
|
6565
|
+
ring.push(key);
|
|
6566
|
+
rings.set(value.path, ring);
|
|
6567
|
+
// The last N versions, and apart from them the last N bases: a base
|
|
6568
|
+
// is what a slower tier answered (the bundle's own object, held
|
|
6569
|
+
// anyway) and written up here, and it must not push out the version
|
|
6570
|
+
// before the newest -- which a reader of the previous version still
|
|
6571
|
+
// asks for.
|
|
6572
|
+
var isBase = value.version.chain === EMPTY_CHAIN;
|
|
6573
|
+
var sameKind = ring.filter(function (held) {
|
|
6574
|
+
var _versionOf;
|
|
6575
|
+
return ((_versionOf = versionOf(held)) === null || _versionOf === void 0 ? void 0 : _versionOf.chain) === EMPTY_CHAIN === isBase;
|
|
6576
|
+
});
|
|
6577
|
+
var _iterator3 = createForOfIteratorHelper._createForOfIteratorHelper(sameKind.slice(0, Math.max(0, sameKind.length - opts.lastPerModule))),
|
|
6578
|
+
_step3;
|
|
6579
|
+
try {
|
|
6580
|
+
for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
|
|
6581
|
+
var oldest = _step3.value;
|
|
6582
|
+
drop(oldest);
|
|
6583
|
+
}
|
|
6584
|
+
} catch (err) {
|
|
6585
|
+
_iterator3.e(err);
|
|
6586
|
+
} finally {
|
|
6587
|
+
_iterator3.f();
|
|
6588
|
+
}
|
|
6589
|
+
var _iterator4 = createForOfIteratorHelper._createForOfIteratorHelper(held.keys()),
|
|
6590
|
+
_step4;
|
|
6591
|
+
try {
|
|
6592
|
+
for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) {
|
|
6593
|
+
var lru = _step4.value;
|
|
6594
|
+
if (bytes <= opts.budgetBytes) break;
|
|
6595
|
+
if (lru === key) continue;
|
|
6596
|
+
drop(lru);
|
|
6597
|
+
}
|
|
6598
|
+
} catch (err) {
|
|
6599
|
+
_iterator4.e(err);
|
|
6600
|
+
} finally {
|
|
6601
|
+
_iterator4.f();
|
|
6602
|
+
}
|
|
6603
|
+
},
|
|
6604
|
+
charge: function charge(key, extra) {
|
|
6605
|
+
var entry = held.get(key);
|
|
6606
|
+
if (entry === undefined) return;
|
|
6607
|
+
entry.bytes += extra;
|
|
6608
|
+
bytes += extra;
|
|
6609
|
+
},
|
|
6610
|
+
latest: function latest(path) {
|
|
6611
|
+
var _held$get$value, _held$get2;
|
|
6612
|
+
var ring = rings.get(path);
|
|
6613
|
+
var newest = ring === null || ring === void 0 ? void 0 : ring[ring.length - 1];
|
|
6614
|
+
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;
|
|
6615
|
+
},
|
|
6616
|
+
stats: function stats() {
|
|
6617
|
+
// Versions: what a chain made. A base written up from a slower tier is
|
|
6618
|
+
// the build's own object (counted at no bytes), held beside them, and
|
|
6619
|
+
// not one of the module's versions.
|
|
6620
|
+
var mostVersions = 0;
|
|
6621
|
+
var mostBytes = 0;
|
|
6622
|
+
var versions = 0;
|
|
6623
|
+
var modules = 0;
|
|
6624
|
+
var _iterator5 = createForOfIteratorHelper._createForOfIteratorHelper(rings.values()),
|
|
6625
|
+
_step5;
|
|
6626
|
+
try {
|
|
6627
|
+
for (_iterator5.s(); !(_step5 = _iterator5.n()).done;) {
|
|
6628
|
+
var ring = _step5.value;
|
|
6629
|
+
var made = ring.filter(function (key) {
|
|
6630
|
+
var _versionOf2;
|
|
6631
|
+
return ((_versionOf2 = versionOf(key)) === null || _versionOf2 === void 0 ? void 0 : _versionOf2.chain) !== EMPTY_CHAIN;
|
|
6632
|
+
});
|
|
6633
|
+
versions += made.length;
|
|
6634
|
+
if (made.length > 0) modules++;
|
|
6635
|
+
mostVersions = Math.max(mostVersions, made.length);
|
|
6636
|
+
var moduleBytes = 0;
|
|
6637
|
+
var _iterator6 = createForOfIteratorHelper._createForOfIteratorHelper(ring),
|
|
6638
|
+
_step6;
|
|
6639
|
+
try {
|
|
6640
|
+
for (_iterator6.s(); !(_step6 = _iterator6.n()).done;) {
|
|
6641
|
+
var _held$get$bytes, _held$get3;
|
|
6642
|
+
var _key = _step6.value;
|
|
6643
|
+
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;
|
|
6644
|
+
}
|
|
6645
|
+
} catch (err) {
|
|
6646
|
+
_iterator6.e(err);
|
|
6647
|
+
} finally {
|
|
6648
|
+
_iterator6.f();
|
|
6649
|
+
}
|
|
6650
|
+
mostBytes = Math.max(mostBytes, moduleBytes);
|
|
6651
|
+
}
|
|
6652
|
+
} catch (err) {
|
|
6653
|
+
_iterator5.e(err);
|
|
6654
|
+
} finally {
|
|
6655
|
+
_iterator5.f();
|
|
6656
|
+
}
|
|
6657
|
+
return {
|
|
6658
|
+
bytes: bytes,
|
|
6659
|
+
versions: versions,
|
|
6660
|
+
modules: modules,
|
|
6661
|
+
mostVersionsOfOneModule: mostVersions,
|
|
6662
|
+
mostBytesOfOneModule: mostBytes,
|
|
6663
|
+
evicted: evicted
|
|
6664
|
+
};
|
|
6665
|
+
}
|
|
6666
|
+
};
|
|
6667
|
+
}
|
|
6668
|
+
|
|
6669
|
+
// #region bundle
|
|
6670
|
+
|
|
6671
|
+
/**
|
|
6672
|
+
* The build's own modules, free: answers only a key whose version is the
|
|
6673
|
+
* bundle's -- its schema, its base and the empty chain -- with the bundle's
|
|
6674
|
+
* own object, which the process holds for every visitor anyway. `bundle`
|
|
6675
|
+
* says what the build evaluated each module to; `null` for a module the
|
|
6676
|
+
* build does not have.
|
|
6677
|
+
*/
|
|
6678
|
+
function bundleTier(bundle) {
|
|
6679
|
+
var name = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : "bundle";
|
|
6680
|
+
return {
|
|
6681
|
+
name: name,
|
|
6682
|
+
get: function get(key) {
|
|
6683
|
+
return createForOfIteratorHelper._asyncToGenerator(/*#__PURE__*/createForOfIteratorHelper._regenerator().m(function _callee2() {
|
|
6684
|
+
var path, own, version;
|
|
6685
|
+
return createForOfIteratorHelper._regenerator().w(function (_context2) {
|
|
6686
|
+
while (1) switch (_context2.n) {
|
|
6687
|
+
case 0:
|
|
6688
|
+
path = pathOfModuleKey(key);
|
|
6689
|
+
if (!(path === null)) {
|
|
6690
|
+
_context2.n = 1;
|
|
6691
|
+
break;
|
|
6692
|
+
}
|
|
6693
|
+
return _context2.a(2, null);
|
|
6694
|
+
case 1:
|
|
6695
|
+
_context2.n = 2;
|
|
6696
|
+
return bundle(path);
|
|
6697
|
+
case 2:
|
|
6698
|
+
own = _context2.v;
|
|
6699
|
+
if (!(own === null)) {
|
|
6700
|
+
_context2.n = 3;
|
|
6701
|
+
break;
|
|
6702
|
+
}
|
|
6703
|
+
return _context2.a(2, null);
|
|
6704
|
+
case 3:
|
|
6705
|
+
version = createForOfIteratorHelper._objectSpread2(createForOfIteratorHelper._objectSpread2({}, own.version), {}, {
|
|
6706
|
+
chain: EMPTY_CHAIN
|
|
6707
|
+
});
|
|
6708
|
+
if (!(moduleKey(path, version) !== key)) {
|
|
6709
|
+
_context2.n = 4;
|
|
6710
|
+
break;
|
|
6711
|
+
}
|
|
6712
|
+
return _context2.a(2, null);
|
|
6713
|
+
case 4:
|
|
6714
|
+
return _context2.a(2, {
|
|
6715
|
+
path: path,
|
|
6716
|
+
version: version,
|
|
6717
|
+
content: baseModuleContent(own.source),
|
|
6718
|
+
patches: [],
|
|
6719
|
+
bytes: 0
|
|
6720
|
+
});
|
|
6721
|
+
}
|
|
6722
|
+
}, _callee2);
|
|
6723
|
+
}))();
|
|
6724
|
+
}
|
|
6725
|
+
};
|
|
6726
|
+
}
|
|
6727
|
+
|
|
6728
|
+
/** The module a {@link moduleKey} is the address of, or `null` for none. */
|
|
6729
|
+
function pathOfModuleKey(key) {
|
|
6730
|
+
var at = key.lastIndexOf("@");
|
|
6731
|
+
return at <= 0 ? null : key.slice(0, at);
|
|
6732
|
+
}
|
|
6733
|
+
|
|
6734
|
+
/**
|
|
6735
|
+
* The version a {@link moduleKey} names, or `null` when it is not one. The
|
|
6736
|
+
* three parts are hex or empty, so the split is unambiguous.
|
|
6737
|
+
*/
|
|
6738
|
+
function parseModuleKey(key) {
|
|
6739
|
+
var path = pathOfModuleKey(key);
|
|
6740
|
+
if (path === null) return null;
|
|
6741
|
+
var parts = key.slice(path.length + 1).split(".");
|
|
6742
|
+
var _parts = createForOfIteratorHelper._slicedToArray(parts, 3),
|
|
6743
|
+
schema = _parts[0],
|
|
6744
|
+
base = _parts[1],
|
|
6745
|
+
chain = _parts[2];
|
|
6746
|
+
if (parts.length !== 3 || schema === undefined || base === undefined || chain === undefined) {
|
|
6747
|
+
return null;
|
|
6748
|
+
}
|
|
6749
|
+
return {
|
|
6750
|
+
path: path,
|
|
6751
|
+
version: {
|
|
6752
|
+
schema: schema,
|
|
6753
|
+
base: base,
|
|
6754
|
+
chain: chain
|
|
6755
|
+
}
|
|
6756
|
+
};
|
|
6757
|
+
}
|
|
6758
|
+
|
|
6759
|
+
// #region entries
|
|
6760
|
+
|
|
6761
|
+
/**
|
|
6762
|
+
* A `.jsonValues()` entry's version: the record's schema and base, the
|
|
6763
|
+
* entry's key, and the chain of the record's patches that touched THIS
|
|
6764
|
+
* entry -- so an entry no patch touched keeps its version while the record's
|
|
6765
|
+
* chain grows, and two reads at one entry version are the same content.
|
|
6766
|
+
* Entries are stored on their own, so a large record is not copied because
|
|
6767
|
+
* one entry changed.
|
|
6768
|
+
*/
|
|
6769
|
+
|
|
6770
|
+
/** The address of an entry's content. Never changes meaning. */
|
|
6771
|
+
function entryAddress(path, v) {
|
|
6772
|
+
return "".concat(moduleKey(path, v), "#").concat(v.key);
|
|
6773
|
+
}
|
|
6774
|
+
|
|
6775
|
+
/** Whether two entry versions name the same content. */
|
|
6776
|
+
function sameEntryVersion(a, b) {
|
|
6777
|
+
return a.key === b.key && a.schema === b.schema && a.base === b.base && a.chain === b.chain;
|
|
6778
|
+
}
|
|
6779
|
+
|
|
6780
|
+
var SharedValConfig = zod.z.object({
|
|
6781
|
+
contentHostUrl: zod.z.string().optional(),
|
|
6782
|
+
appHostUrl: zod.z.string().optional(),
|
|
6783
|
+
project: zod.z.string().optional(),
|
|
4760
6784
|
root: zod.z.string().optional(),
|
|
4761
6785
|
gitCommit: zod.z.string().optional(),
|
|
4762
6786
|
gitBranch: zod.z.string().optional(),
|
|
@@ -6237,24 +8261,30 @@ function emptyRecord(schema, context) {
|
|
|
6237
8261
|
}));
|
|
6238
8262
|
}
|
|
6239
8263
|
|
|
6240
|
-
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"];
|
|
8264
|
+
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"];
|
|
6241
8265
|
|
|
6242
8266
|
/**
|
|
6243
8267
|
* A page sketch: a page written as indented text, with four primitives.
|
|
6244
8268
|
*
|
|
6245
8269
|
* ```
|
|
8270
|
+
* --- /about a route: what follows is the page at /about
|
|
6246
8271
|
* # Our people text (a heading: #, ## or ###)
|
|
6247
8272
|
* We are 40 people in Oslo. text (a paragraph)
|
|
8273
|
+
* ## Hi {field: /x.val.ts?p="t"} text linked to a content field: typing
|
|
8274
|
+
* in it writes the field
|
|
6248
8275
|
* [image: Team photo] placeholder: [kind] or [kind: label]
|
|
6249
8276
|
* [hero: Roof](image: /a.jpg) a placeholder with an image (or video) in it
|
|
6250
8277
|
* icon hero M3 5h18v14H3z how every [hero] box is drawn: SVG path data
|
|
6251
8278
|
* row its children side by side
|
|
6252
|
-
* col
|
|
6253
|
-
* ## Design
|
|
8279
|
+
* col its children stacked; columns share a row
|
|
8280
|
+
* ## Design equally
|
|
6254
8281
|
* col
|
|
6255
8282
|
* [button: Join us]
|
|
6256
8283
|
* ```
|
|
6257
8284
|
*
|
|
8285
|
+
* A sketchpad is not one page's: a route divider starts the part about a
|
|
8286
|
+
* page, so one sketch can cover a page and the two it links to.
|
|
8287
|
+
*
|
|
6258
8288
|
* It is a scratch pad, not a layout language: it says WHAT is on a page and
|
|
6259
8289
|
* roughly WHERE, so an assistant and a person can agree on a page's content
|
|
6260
8290
|
* before anyone worries about how it looks. The person never sees this text,
|
|
@@ -6273,7 +8303,21 @@ var toolNames = ["get_all_schema", "get_source", "search_content", "validate_con
|
|
|
6273
8303
|
* that builds the page reads it exactly as the person left it.
|
|
6274
8304
|
*/
|
|
6275
8305
|
|
|
8306
|
+
/** The start of the part about one page. Only at the top level. */
|
|
8307
|
+
|
|
8308
|
+
/** Columns share their row equally: a sketch says what, not how wide. */
|
|
8309
|
+
|
|
8310
|
+
/**
|
|
8311
|
+
* `src` names a file the project has, never a guess: its `path` exactly as
|
|
8312
|
+
* the content has it (`/public/val/hero.webp`, or its remote URL), or an
|
|
8313
|
+
* image uploaded to the assistant's session, `session:<key>`. Which files
|
|
8314
|
+
* those are is the Studio's to check; the text only holds the name.
|
|
8315
|
+
*/
|
|
8316
|
+
|
|
8317
|
+
/** The prefix of a `src` that is an image in the assistant's session. */
|
|
8318
|
+
var SESSION_MEDIA_PREFIX = "session:";
|
|
6276
8319
|
var CONTAINER = /^(row|col)(?:\s+(\d+))?$/;
|
|
8320
|
+
var ROUTE = /^---(?:\s+(.*))?$/;
|
|
6277
8321
|
var PLACEHOLDER = /^\[([^\]:]*)(?::([^\]]*))?\](?:\((image|video):\s*([^\s)]+)\s*\))?$/;
|
|
6278
8322
|
var HAS_MEDIA = /^\[[^\]]*\]\(/;
|
|
6279
8323
|
var ICON = /^icon\s+(\S+)\s+([Mm][\d\s.-].*)$/;
|
|
@@ -6316,6 +8360,32 @@ function parseSketch(source) {
|
|
|
6316
8360
|
while (stack.length > 1 && indent <= stack[stack.length - 1].indent) {
|
|
6317
8361
|
stack.pop();
|
|
6318
8362
|
}
|
|
8363
|
+
var route = ROUTE.exec(content);
|
|
8364
|
+
if (route) {
|
|
8365
|
+
var _route$;
|
|
8366
|
+
var _path = ((_route$ = route[1]) !== null && _route$ !== void 0 ? _route$ : "").trim();
|
|
8367
|
+
if (!_path.startsWith("/") || /\s/.test(_path)) {
|
|
8368
|
+
errors.push({
|
|
8369
|
+
line: line,
|
|
8370
|
+
message: 'A route divider names one page by its route, like "--- /about".'
|
|
8371
|
+
});
|
|
8372
|
+
}
|
|
8373
|
+
if (indent > 0) {
|
|
8374
|
+
errors.push({
|
|
8375
|
+
line: line,
|
|
8376
|
+
message: "A route divider starts a page, so it goes at the top level, not indented."
|
|
8377
|
+
});
|
|
8378
|
+
}
|
|
8379
|
+
// It closes everything open: what follows is another page.
|
|
8380
|
+
stack.length = 1;
|
|
8381
|
+
stack[0].lastIndent = 0;
|
|
8382
|
+
blocks.push({
|
|
8383
|
+
type: "route",
|
|
8384
|
+
line: line,
|
|
8385
|
+
route: _path
|
|
8386
|
+
});
|
|
8387
|
+
return;
|
|
8388
|
+
}
|
|
6319
8389
|
var parent = stack[stack.length - 1];
|
|
6320
8390
|
var node = parseContent(content, line, errors);
|
|
6321
8391
|
|
|
@@ -6357,7 +8427,27 @@ function parseSketch(source) {
|
|
|
6357
8427
|
errors: errors
|
|
6358
8428
|
};
|
|
6359
8429
|
}
|
|
6360
|
-
|
|
8430
|
+
|
|
8431
|
+
/** The link to a content field at the end of a text line. */
|
|
8432
|
+
var FIELD = / \{field: (\/[^{}]*)\}$/;
|
|
8433
|
+
function parseContent(whole, line, errors) {
|
|
8434
|
+
var link = FIELD.exec(whole);
|
|
8435
|
+
if (link) {
|
|
8436
|
+
var node = parseLine(whole.slice(0, link.index), line, errors);
|
|
8437
|
+
if (node.type !== "text") {
|
|
8438
|
+
errors.push({
|
|
8439
|
+
line: line,
|
|
8440
|
+
message: "Only a heading or a paragraph is linked to a field: put {field: ...} at the end of one."
|
|
8441
|
+
});
|
|
8442
|
+
return node;
|
|
8443
|
+
}
|
|
8444
|
+
return createForOfIteratorHelper._objectSpread2(createForOfIteratorHelper._objectSpread2({}, node), {}, {
|
|
8445
|
+
field: link[1].trim()
|
|
8446
|
+
});
|
|
8447
|
+
}
|
|
8448
|
+
return parseLine(whole, line, errors);
|
|
8449
|
+
}
|
|
8450
|
+
function parseLine(content, line, errors) {
|
|
6361
8451
|
// A leading backslash keeps a line that looks like a keyword as text.
|
|
6362
8452
|
if (content.startsWith("\\")) {
|
|
6363
8453
|
return {
|
|
@@ -6382,17 +8472,15 @@ function parseContent(content, line, errors) {
|
|
|
6382
8472
|
children: []
|
|
6383
8473
|
};
|
|
6384
8474
|
}
|
|
6385
|
-
|
|
6386
|
-
if (weight === 0) {
|
|
8475
|
+
if (container[2] !== undefined) {
|
|
6387
8476
|
errors.push({
|
|
6388
8477
|
line: line,
|
|
6389
|
-
message:
|
|
8478
|
+
message: 'Columns share a row equally, so a col takes no number: write "col".'
|
|
6390
8479
|
});
|
|
6391
8480
|
}
|
|
6392
8481
|
return {
|
|
6393
8482
|
type: "col",
|
|
6394
8483
|
line: line,
|
|
6395
|
-
weight: Math.max(weight, 1),
|
|
6396
8484
|
children: []
|
|
6397
8485
|
};
|
|
6398
8486
|
}
|
|
@@ -6466,13 +8554,19 @@ function parseContent(content, line, errors) {
|
|
|
6466
8554
|
* The line a text block is written as. Line breaks become spaces, and a
|
|
6467
8555
|
* paragraph that would read as something else is kept as text with `\`.
|
|
6468
8556
|
*/
|
|
6469
|
-
function formatSketchText(level, text) {
|
|
8557
|
+
function formatSketchText(level, text, field) {
|
|
6470
8558
|
var flat = text.replace(/\s+/g, " ").trim();
|
|
8559
|
+
var path = field === null || field === void 0 ? void 0 : field.replace(/\s+/g, " ").trim();
|
|
8560
|
+
// A source path, and one the line can be read back with: no braces.
|
|
8561
|
+
var link = path && /^\/[^{}]*$/.test(path) ? " {field: ".concat(path, "}") : "";
|
|
6471
8562
|
if (level > 0) {
|
|
6472
|
-
return "#".repeat(level) + " " + flat;
|
|
8563
|
+
return "#".repeat(level) + " " + flat + link;
|
|
6473
8564
|
}
|
|
6474
|
-
|
|
6475
|
-
|
|
8565
|
+
// Read as the whole line will be, link and all: `icon x M` is text, and
|
|
8566
|
+
// `icon x M {field: ...}` would be an icon.
|
|
8567
|
+
var whole = flat + link;
|
|
8568
|
+
var ambiguous = flat.startsWith("\\") || flat.startsWith("[") || CONTAINER.test(whole) || ROUTE.test(whole) || HEADING.test(whole) || ICON.test(whole);
|
|
8569
|
+
return (ambiguous ? "\\" + flat : flat) + link;
|
|
6476
8570
|
}
|
|
6477
8571
|
|
|
6478
8572
|
/** The line a placeholder is written as. */
|
|
@@ -6497,17 +8591,26 @@ function formatSketchBox(placeholder) {
|
|
|
6497
8591
|
*/
|
|
6498
8592
|
function formatSketch(blocks, icons) {
|
|
6499
8593
|
var lines = [];
|
|
8594
|
+
// One block per field: the first keeps the link.
|
|
8595
|
+
var linked = new Set();
|
|
6500
8596
|
var _walk = function walk(node, depth, inRow) {
|
|
6501
8597
|
var indent = " ".repeat(depth);
|
|
6502
8598
|
switch (node.type) {
|
|
6503
8599
|
case "text":
|
|
6504
8600
|
if (node.text) {
|
|
6505
|
-
|
|
8601
|
+
var field = node.field && !linked.has(node.field) ? node.field : undefined;
|
|
8602
|
+
if (field) {
|
|
8603
|
+
linked.add(field);
|
|
8604
|
+
}
|
|
8605
|
+
lines.push(indent + formatSketchText(node.level, node.text, field));
|
|
6506
8606
|
}
|
|
6507
8607
|
return;
|
|
6508
8608
|
case "placeholder":
|
|
6509
8609
|
lines.push(indent + formatSketchBox(node));
|
|
6510
8610
|
return;
|
|
8611
|
+
case "route":
|
|
8612
|
+
lines.push("--- ".concat(node.route));
|
|
8613
|
+
return;
|
|
6511
8614
|
case "row":
|
|
6512
8615
|
lines.push(indent + "row");
|
|
6513
8616
|
if (node.children.length === 0) {
|
|
@@ -6524,7 +8627,7 @@ function formatSketch(blocks, icons) {
|
|
|
6524
8627
|
});
|
|
6525
8628
|
return;
|
|
6526
8629
|
}
|
|
6527
|
-
lines.push(indent +
|
|
8630
|
+
lines.push(indent + "col");
|
|
6528
8631
|
node.children.forEach(function (child) {
|
|
6529
8632
|
return _walk(child, depth + 1, false);
|
|
6530
8633
|
});
|
|
@@ -6544,6 +8647,33 @@ function formatSketch(blocks, icons) {
|
|
|
6544
8647
|
return all.length > 0 ? all.join("\n") + "\n" : "";
|
|
6545
8648
|
}
|
|
6546
8649
|
|
|
8650
|
+
/**
|
|
8651
|
+
* The text of every block linked to a field, by field: what the Studio
|
|
8652
|
+
* writes to the content when a linked block changes. The first block wins,
|
|
8653
|
+
* as `formatSketch` keeps only its link.
|
|
8654
|
+
*/
|
|
8655
|
+
function sketchFieldTexts(blocks) {
|
|
8656
|
+
var texts = new Map();
|
|
8657
|
+
var _walk2 = function walk(node) {
|
|
8658
|
+
if (node.type === "text") {
|
|
8659
|
+
if (node.field && node.text && !texts.has(node.field)) {
|
|
8660
|
+
texts.set(node.field, node.text.replace(/\s+/g, " ").trim());
|
|
8661
|
+
}
|
|
8662
|
+
} else if (node.type === "row" || node.type === "col") {
|
|
8663
|
+
node.children.forEach(_walk2);
|
|
8664
|
+
}
|
|
8665
|
+
};
|
|
8666
|
+
blocks.forEach(_walk2);
|
|
8667
|
+
return texts;
|
|
8668
|
+
}
|
|
8669
|
+
|
|
8670
|
+
/** The pages a sketch is about, in the order it gives them. */
|
|
8671
|
+
function sketchRoutes(blocks) {
|
|
8672
|
+
return blocks.flatMap(function (block) {
|
|
8673
|
+
return block.type === "route" && block.route !== "" ? [block.route] : [];
|
|
8674
|
+
});
|
|
8675
|
+
}
|
|
8676
|
+
|
|
6547
8677
|
/**
|
|
6548
8678
|
* What a list names a sketch by: its first top-level heading, the biggest
|
|
6549
8679
|
* one first. Null for a sketch with no heading, which a list then names by
|
|
@@ -7969,7 +10099,9 @@ function createSearchIndex() {
|
|
|
7969
10099
|
tokenize: "forward"
|
|
7970
10100
|
}),
|
|
7971
10101
|
pathToLabel: new Map(),
|
|
7972
|
-
docsByModule: new Map()
|
|
10102
|
+
docsByModule: new Map(),
|
|
10103
|
+
walkedByModule: new Map(),
|
|
10104
|
+
entriesByModule: new Map()
|
|
7973
10105
|
};
|
|
7974
10106
|
}
|
|
7975
10107
|
|
|
@@ -7983,77 +10115,230 @@ function createSearchIndex() {
|
|
|
7983
10115
|
* `.jsonValues()` entries are indexed only once their content is loaded — an
|
|
7984
10116
|
* un-loaded entry is an opaque marker that {@link traverseSchemaSource} skips, so
|
|
7985
10117
|
* the index is naturally PARTIAL and grows as batches land. That is why the search
|
|
7986
|
-
* UI loads the entries on the first query and re-indexes
|
|
7987
|
-
*
|
|
10118
|
+
* UI loads the entries on the first query and re-indexes as they arrive, rather
|
|
10119
|
+
* than pretending an empty result set is an answer -- and why a module whose
|
|
10120
|
+
* root is a record is re-indexed by entry ({@link indexRecordModule}): a pass
|
|
10121
|
+
* per batch walks the batch, not the record.
|
|
7988
10122
|
*/
|
|
7989
|
-
function indexModule(searchIndex, moduleFilePath, source, schema
|
|
7990
|
-
|
|
7991
|
-
|
|
7992
|
-
|
|
10123
|
+
function indexModule(searchIndex, moduleFilePath, source, schema,
|
|
10124
|
+
/**
|
|
10125
|
+
* Stop by this time (`performance.now()`), for a caller that must not hold
|
|
10126
|
+
* the thread: answers `false` when it stopped with entries of a record
|
|
10127
|
+
* module still to walk, and the same call again goes on from there. A
|
|
10128
|
+
* module that is not a record is walked whole, whatever the time.
|
|
10129
|
+
*/
|
|
10130
|
+
until) {
|
|
10131
|
+
var walked = searchIndex.walkedByModule.get(moduleFilePath);
|
|
10132
|
+
if (walked !== undefined && walked.source === source && walked.schema === schemaKeyOf(schema)) {
|
|
10133
|
+
return true;
|
|
10134
|
+
}
|
|
10135
|
+
if (schema.type === "record" && isJsonRecord(source)) {
|
|
10136
|
+
var done = indexRecordModule(searchIndex, moduleFilePath, source, schema, until);
|
|
10137
|
+
if (done) {
|
|
10138
|
+
searchIndex.walkedByModule.set(moduleFilePath, {
|
|
10139
|
+
source: source,
|
|
10140
|
+
schema: schemaKeyOf(schema)
|
|
10141
|
+
});
|
|
10142
|
+
}
|
|
10143
|
+
return done;
|
|
10144
|
+
}
|
|
7993
10145
|
removeModule(searchIndex, moduleFilePath);
|
|
7994
10146
|
var docs = new Set();
|
|
7995
|
-
docsByModule.set(moduleFilePath, docs);
|
|
10147
|
+
searchIndex.docsByModule.set(moduleFilePath, docs);
|
|
7996
10148
|
var path = moduleFilePath + "?p=";
|
|
7997
|
-
traverseSchemaSource(source, schema, path, function (
|
|
7998
|
-
var
|
|
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
|
-
|
|
8036
|
-
|
|
8037
|
-
|
|
8038
|
-
|
|
8039
|
-
|
|
8040
|
-
|
|
8041
|
-
|
|
8042
|
-
|
|
10149
|
+
traverseSchemaSource(source, schema, path, function (leaf) {
|
|
10150
|
+
var id = indexLeaf(searchIndex, leaf);
|
|
10151
|
+
if (id !== null) docs.add(id);
|
|
10152
|
+
});
|
|
10153
|
+
searchIndex.walkedByModule.set(moduleFilePath, {
|
|
10154
|
+
source: source,
|
|
10155
|
+
schema: schemaKeyOf(schema)
|
|
10156
|
+
});
|
|
10157
|
+
return true;
|
|
10158
|
+
}
|
|
10159
|
+
|
|
10160
|
+
/** How many entries are walked between two looks at the clock. */
|
|
10161
|
+
var ENTRIES_PER_CLOCK_CHECK = 32;
|
|
10162
|
+
|
|
10163
|
+
/**
|
|
10164
|
+
* A module whose root is a record, indexed entry by entry: an entry whose
|
|
10165
|
+
* value is the SAME OBJECT it was indexed from keeps its documents, and only
|
|
10166
|
+
* the others are walked.
|
|
10167
|
+
*
|
|
10168
|
+
* What makes loading a `.jsonValues()` record of 10,000 entries searchable
|
|
10169
|
+
* while it loads. The Studio asks again as each batch lands, and the record's
|
|
10170
|
+
* value is copy-on-write -- an entry nothing touched is the object it was --
|
|
10171
|
+
* so each pass indexes the batch that arrived, not the record. Walked whole,
|
|
10172
|
+
* that was the whole record per batch: quadratic, and a long task each time.
|
|
10173
|
+
*
|
|
10174
|
+
* Identity is only ever a shortcut: a value that is equal but a new object
|
|
10175
|
+
* (a structured clone across a worker, a module read again) is walked again,
|
|
10176
|
+
* never skipped. A different schema starts the module over.
|
|
10177
|
+
*/
|
|
10178
|
+
function indexRecordModule(searchIndex, moduleFilePath, source, schema, until) {
|
|
10179
|
+
var index = searchIndex.index,
|
|
10180
|
+
pathToLabel = searchIndex.pathToLabel,
|
|
10181
|
+
docsByModule = searchIndex.docsByModule,
|
|
10182
|
+
entriesByModule = searchIndex.entriesByModule;
|
|
10183
|
+
var indexed = entriesByModule.get(moduleFilePath);
|
|
10184
|
+
var docs = docsByModule.get(moduleFilePath);
|
|
10185
|
+
if (indexed === undefined || docs === undefined || indexed.schema !== schema && schemaKeyOf(indexed.schema) !== schemaKeyOf(schema)) {
|
|
10186
|
+
removeModule(searchIndex, moduleFilePath);
|
|
10187
|
+
indexed = {
|
|
10188
|
+
schema: schema,
|
|
10189
|
+
entries: new Map()
|
|
10190
|
+
};
|
|
10191
|
+
docs = new Set();
|
|
10192
|
+
entriesByModule.set(moduleFilePath, indexed);
|
|
10193
|
+
docsByModule.set(moduleFilePath, docs);
|
|
10194
|
+
}
|
|
10195
|
+
var _indexed = indexed,
|
|
10196
|
+
entries = _indexed.entries;
|
|
10197
|
+
var _iterator = createForOfIteratorHelper._createForOfIteratorHelper(entries),
|
|
10198
|
+
_step;
|
|
10199
|
+
try {
|
|
10200
|
+
for (_iterator.s(); !(_step = _iterator.n()).done;) {
|
|
10201
|
+
var _step$value = createForOfIteratorHelper._slicedToArray(_step.value, 2),
|
|
10202
|
+
_key = _step$value[0],
|
|
10203
|
+
was = _step$value[1];
|
|
10204
|
+
if (Object.prototype.hasOwnProperty.call(source, _key) && source[_key] === was.source) {
|
|
10205
|
+
continue;
|
|
10206
|
+
}
|
|
10207
|
+
var _iterator2 = createForOfIteratorHelper._createForOfIteratorHelper(was.docs),
|
|
10208
|
+
_step2;
|
|
10209
|
+
try {
|
|
10210
|
+
for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
|
|
10211
|
+
var _id = _step2.value;
|
|
10212
|
+
index.remove(_id);
|
|
10213
|
+
pathToLabel["delete"](_id);
|
|
10214
|
+
docs["delete"](_id);
|
|
10215
|
+
}
|
|
10216
|
+
} catch (err) {
|
|
10217
|
+
_iterator2.e(err);
|
|
10218
|
+
} finally {
|
|
10219
|
+
_iterator2.f();
|
|
8043
10220
|
}
|
|
10221
|
+
entries["delete"](_key);
|
|
8044
10222
|
}
|
|
10223
|
+
} catch (err) {
|
|
10224
|
+
_iterator.e(err);
|
|
10225
|
+
} finally {
|
|
10226
|
+
_iterator.f();
|
|
10227
|
+
}
|
|
10228
|
+
var path = moduleFilePath + "?p=";
|
|
10229
|
+
var walked = 0;
|
|
10230
|
+
var _loop = function _loop() {
|
|
10231
|
+
if (entries.has(key)) return 0; // continue
|
|
10232
|
+
if (until !== undefined && ++walked % ENTRIES_PER_CLOCK_CHECK === 0 && performance.now() > until) {
|
|
10233
|
+
return {
|
|
10234
|
+
v: false
|
|
10235
|
+
};
|
|
10236
|
+
}
|
|
10237
|
+
var value = source[key];
|
|
10238
|
+
var entryDocs = [];
|
|
10239
|
+
traverseSchemaSource(value, schema.item, sourcePathOfChild(path, key), function (leaf) {
|
|
10240
|
+
var id = indexLeaf(searchIndex, leaf);
|
|
10241
|
+
if (id === null) return;
|
|
10242
|
+
entryDocs.push(id);
|
|
10243
|
+
docs.add(id);
|
|
10244
|
+
});
|
|
10245
|
+
entries.set(key, {
|
|
10246
|
+
source: value,
|
|
10247
|
+
docs: entryDocs
|
|
10248
|
+
});
|
|
10249
|
+
},
|
|
10250
|
+
_ret;
|
|
10251
|
+
for (var key in source) {
|
|
10252
|
+
_ret = _loop();
|
|
10253
|
+
if (_ret === 0) continue;
|
|
10254
|
+
if (_ret) return _ret.v;
|
|
10255
|
+
}
|
|
10256
|
+
return true;
|
|
10257
|
+
}
|
|
10258
|
+
function isJsonRecord(source) {
|
|
10259
|
+
return createForOfIteratorHelper._typeof(source) === "object" && source !== null && !Array.isArray(source);
|
|
10260
|
+
}
|
|
10261
|
+
|
|
10262
|
+
/**
|
|
10263
|
+
* A schema by its content, kept per schema object. Its object is not enough:
|
|
10264
|
+
* the Studio receives a module's schema again (new code, a re-read of the
|
|
10265
|
+
* project) as a new object that says the same, and starting a record of
|
|
10266
|
+
* 10,000 entries over for that was seconds of removing and adding
|
|
10267
|
+
* documents in one go.
|
|
10268
|
+
*/
|
|
10269
|
+
var schemaKeys = new WeakMap();
|
|
10270
|
+
function schemaKeyOf(schema) {
|
|
10271
|
+
var key = schemaKeys.get(schema);
|
|
10272
|
+
if (key === undefined) {
|
|
10273
|
+
key = JSON.stringify(schema);
|
|
10274
|
+
schemaKeys.set(schema, key);
|
|
10275
|
+
}
|
|
10276
|
+
return key;
|
|
10277
|
+
}
|
|
10278
|
+
|
|
10279
|
+
/** One leaf as a document, if it has anything to find. Its id, or `null`. */
|
|
10280
|
+
function indexLeaf(searchIndex, _ref) {
|
|
10281
|
+
var source = _ref.source,
|
|
10282
|
+
schema = _ref.schema,
|
|
10283
|
+
path = _ref.path;
|
|
10284
|
+
if (source === null) {
|
|
10285
|
+
return null;
|
|
10286
|
+
}
|
|
10287
|
+
var index = searchIndex.index,
|
|
10288
|
+
pathToLabel = searchIndex.pathToLabel;
|
|
10289
|
+
var searchText = "";
|
|
10290
|
+
var label = "";
|
|
8045
10291
|
|
|
8046
|
-
|
|
8047
|
-
|
|
8048
|
-
|
|
8049
|
-
|
|
8050
|
-
|
|
8051
|
-
|
|
8052
|
-
|
|
8053
|
-
|
|
8054
|
-
|
|
10292
|
+
// Handle primitives
|
|
10293
|
+
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" ||
|
|
10294
|
+
// An enum's value is a string the editor chose; it is as searchable as
|
|
10295
|
+
// any other string leaf. The old string union was never indexed at all,
|
|
10296
|
+
// which is the gap this closes.
|
|
10297
|
+
schema.type === "enum" ||
|
|
10298
|
+
// A locale tag, for the same reason: it is what the editor picked and
|
|
10299
|
+
// what they see in the field, so searching "nb-NO" has to find the
|
|
10300
|
+
// Norwegian rows. `traverseSchemaSource` visits these, and visiting a
|
|
10301
|
+
// leaf only to index it as the empty string is worse than not visiting
|
|
10302
|
+
// it — it looks like content that does not exist.
|
|
10303
|
+
schema.type === "locale") {
|
|
10304
|
+
var _source$toString, _source$toString2;
|
|
10305
|
+
searchText = (_source$toString = source === null || source === void 0 ? void 0 : source.toString()) !== null && _source$toString !== void 0 ? _source$toString : "";
|
|
10306
|
+
label = (_source$toString2 = source === null || source === void 0 ? void 0 : source.toString()) !== null && _source$toString2 !== void 0 ? _source$toString2 : "";
|
|
10307
|
+
} else if (schema.type === "literal") {
|
|
10308
|
+
searchText = schema.value.toString();
|
|
10309
|
+
label = schema.value.toString();
|
|
10310
|
+
}
|
|
10311
|
+
// Handle richtext - flatten to get text content
|
|
10312
|
+
else if (schema.type === "richtext") {
|
|
10313
|
+
searchText = flattenRichText(source);
|
|
10314
|
+
// Use first 50 chars as label
|
|
10315
|
+
label = searchText.substring(0, 50) || "richtext";
|
|
10316
|
+
}
|
|
10317
|
+
// Handle file/image/video
|
|
10318
|
+
else if (schema.type === "file" || schema.type === "image" || schema.type === "video") {
|
|
10319
|
+
if (source !== null && createForOfIteratorHelper._typeof(source) === "object" && "path" in source && typeof source.path === "string") {
|
|
10320
|
+
// The label is the bare filename - the folder is shown separately in
|
|
10321
|
+
// the UI - but both are searchable.
|
|
10322
|
+
var _getRefParts = getRefParts(source.path),
|
|
10323
|
+
filename = _getRefParts.filename,
|
|
10324
|
+
folder = _getRefParts.folder;
|
|
10325
|
+
var alt = "alt" in source ? source.alt : "";
|
|
10326
|
+
searchText = filename + " " + folder + " " + alt;
|
|
10327
|
+
label = filename;
|
|
8055
10328
|
}
|
|
8056
|
-
}
|
|
10329
|
+
}
|
|
10330
|
+
|
|
10331
|
+
// Add to index if we have search text
|
|
10332
|
+
if (!searchText) {
|
|
10333
|
+
return null;
|
|
10334
|
+
}
|
|
10335
|
+
var _Internal$splitModule = core.Internal.splitModuleFilePathAndModulePath(path),
|
|
10336
|
+
_Internal$splitModule2 = createForOfIteratorHelper._slicedToArray(_Internal$splitModule, 2),
|
|
10337
|
+
modulePath = _Internal$splitModule2[1];
|
|
10338
|
+
var cleanPath = fastRemoveNonWordChars(modulePath) + " ";
|
|
10339
|
+
index.add(path, cleanPath + " " + path + " " + searchText);
|
|
10340
|
+
pathToLabel.set(path, label);
|
|
10341
|
+
return path;
|
|
8057
10342
|
}
|
|
8058
10343
|
|
|
8059
10344
|
/**
|
|
@@ -8065,21 +10350,24 @@ function indexModule(searchIndex, moduleFilePath, source, schema) {
|
|
|
8065
10350
|
function removeModule(searchIndex, moduleFilePath) {
|
|
8066
10351
|
var index = searchIndex.index,
|
|
8067
10352
|
pathToLabel = searchIndex.pathToLabel,
|
|
8068
|
-
docsByModule = searchIndex.docsByModule
|
|
10353
|
+
docsByModule = searchIndex.docsByModule,
|
|
10354
|
+
entriesByModule = searchIndex.entriesByModule;
|
|
10355
|
+
entriesByModule["delete"](moduleFilePath);
|
|
10356
|
+
searchIndex.walkedByModule["delete"](moduleFilePath);
|
|
8069
10357
|
var existing = docsByModule.get(moduleFilePath);
|
|
8070
10358
|
if (existing === undefined) return;
|
|
8071
|
-
var
|
|
8072
|
-
|
|
10359
|
+
var _iterator3 = createForOfIteratorHelper._createForOfIteratorHelper(existing),
|
|
10360
|
+
_step3;
|
|
8073
10361
|
try {
|
|
8074
|
-
for (
|
|
8075
|
-
var
|
|
8076
|
-
index.remove(
|
|
8077
|
-
pathToLabel["delete"](
|
|
10362
|
+
for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
|
|
10363
|
+
var _id2 = _step3.value;
|
|
10364
|
+
index.remove(_id2);
|
|
10365
|
+
pathToLabel["delete"](_id2);
|
|
8078
10366
|
}
|
|
8079
10367
|
} catch (err) {
|
|
8080
|
-
|
|
10368
|
+
_iterator3.e(err);
|
|
8081
10369
|
} finally {
|
|
8082
|
-
|
|
10370
|
+
_iterator3.f();
|
|
8083
10371
|
}
|
|
8084
10372
|
docsByModule["delete"](moduleFilePath);
|
|
8085
10373
|
}
|
|
@@ -9554,24 +11842,26 @@ var messageOf = function messageOf(error) {
|
|
|
9554
11842
|
return error instanceof Error ? error.message : String(error);
|
|
9555
11843
|
};
|
|
9556
11844
|
|
|
9557
|
-
exports.PublishProtocolError =
|
|
9558
|
-
exports.
|
|
9559
|
-
exports.
|
|
9560
|
-
exports.
|
|
9561
|
-
exports.
|
|
9562
|
-
exports.
|
|
9563
|
-
exports.
|
|
9564
|
-
exports.
|
|
9565
|
-
exports.
|
|
9566
|
-
exports.
|
|
9567
|
-
exports.
|
|
9568
|
-
exports.
|
|
9569
|
-
exports.
|
|
9570
|
-
exports.
|
|
9571
|
-
exports.
|
|
9572
|
-
exports.
|
|
9573
|
-
exports.
|
|
9574
|
-
exports.
|
|
11845
|
+
exports.PublishProtocolError = projectSource.PublishProtocolError;
|
|
11846
|
+
exports.createProjectSourceReader = projectSource.createProjectSourceReader;
|
|
11847
|
+
exports.parseArtifacts = projectSource.parseArtifacts;
|
|
11848
|
+
exports.parseBuildTarget = projectSource.parseBuildTarget;
|
|
11849
|
+
exports.parseCancel = projectSource.parseCancel;
|
|
11850
|
+
exports.parseDeclare = projectSource.parseDeclare;
|
|
11851
|
+
exports.parseDiscard = projectSource.parseDiscard;
|
|
11852
|
+
exports.parseJob = projectSource.parseJob;
|
|
11853
|
+
exports.parseNewestCiRun = projectSource.parseNewestCiRun;
|
|
11854
|
+
exports.parsePress = projectSource.parsePress;
|
|
11855
|
+
exports.parseProblems = projectSource.parseProblems;
|
|
11856
|
+
exports.parseProjectSource = projectSource.parseProjectSource;
|
|
11857
|
+
exports.parsePromote = projectSource.parsePromote;
|
|
11858
|
+
exports.parsePublishToken = projectSource.parsePublishToken;
|
|
11859
|
+
exports.parseRenew = projectSource.parseRenew;
|
|
11860
|
+
exports.parseRequestStatus = projectSource.parseRequestStatus;
|
|
11861
|
+
exports.parseStatus = projectSource.parseStatus;
|
|
11862
|
+
exports.parseUpdateTarget = projectSource.parseUpdateTarget;
|
|
11863
|
+
exports.parseVerify = projectSource.parseVerify;
|
|
11864
|
+
exports.publishApiOf = projectSource.publishApiOf;
|
|
9575
11865
|
exports.DEFAULT_CANVAS_SELECTION = getSitemapTree.DEFAULT_CANVAS_SELECTION;
|
|
9576
11866
|
exports.DEFAULT_CANVAS_SELECTION_SOFT = getSitemapTree.DEFAULT_CANVAS_SELECTION_SOFT;
|
|
9577
11867
|
exports.QUICK_PREVIEW_PARAM = getSitemapTree.QUICK_PREVIEW_PARAM;
|
|
@@ -9620,12 +11910,15 @@ exports.AnalyticsBeacon = AnalyticsBeacon;
|
|
|
9620
11910
|
exports.Api = Api;
|
|
9621
11911
|
exports.BRAND_CONTRAST_PAIRS = BRAND_CONTRAST_PAIRS;
|
|
9622
11912
|
exports.BinaryFileRef = BinaryFileRef;
|
|
11913
|
+
exports.ChainFold = ChainFold;
|
|
9623
11914
|
exports.CodeOptions = CodeOptions;
|
|
9624
11915
|
exports.ColorOptions = ColorOptions;
|
|
9625
11916
|
exports.DEFAULT_ERROR_MESSAGE = DEFAULT_ERROR_MESSAGE;
|
|
9626
11917
|
exports.DateOptions = DateOptions;
|
|
11918
|
+
exports.EMPTY_CHAIN = EMPTY_CHAIN;
|
|
9627
11919
|
exports.ENCODE_DEFAULTS = ENCODE_DEFAULTS;
|
|
9628
11920
|
exports.ENCODE_MIME_TYPE_OF = ENCODE_MIME_TYPE_OF;
|
|
11921
|
+
exports.ENGINE = ENGINE;
|
|
9629
11922
|
exports.FileOptions = FileOptions;
|
|
9630
11923
|
exports.HistoricalCommit = HistoricalCommit;
|
|
9631
11924
|
exports.HistoricalModule = HistoricalModule;
|
|
@@ -9638,6 +11931,7 @@ exports.JSON_ENTRIES_BATCH_MAX = JSON_ENTRIES_BATCH_MAX;
|
|
|
9638
11931
|
exports.LOCAL_FILE_IN_REMOTE_PROJECT_HINT = LOCAL_FILE_IN_REMOTE_PROJECT_HINT;
|
|
9639
11932
|
exports.LOCAL_FILE_IN_REMOTE_PROJECT_WARNING = LOCAL_FILE_IN_REMOTE_PROJECT_WARNING;
|
|
9640
11933
|
exports.ModuleComparison = ModuleComparison;
|
|
11934
|
+
exports.ModuleVersionSchema = ModuleVersionSchema;
|
|
9641
11935
|
exports.NEVER_ENCODED = NEVER_ENCODED;
|
|
9642
11936
|
exports.ParentRef = ParentRef$1;
|
|
9643
11937
|
exports.Patch = Patch;
|
|
@@ -9646,6 +11940,7 @@ exports.PatchId = PatchId;
|
|
|
9646
11940
|
exports.PatchJSON = PatchJSON;
|
|
9647
11941
|
exports.PatchSets = PatchSets;
|
|
9648
11942
|
exports.PlanRefusalError = PlanRefusalError;
|
|
11943
|
+
exports.ProjectVersionSchema = ProjectVersionSchema;
|
|
9649
11944
|
exports.RAMP_STEPS = RAMP_STEPS;
|
|
9650
11945
|
exports.RemirrorBr = RemirrorBr;
|
|
9651
11946
|
exports.RemirrorBulletList = RemirrorBulletList;
|
|
@@ -9662,7 +11957,9 @@ exports.RestoreVerdict = RestoreVerdict;
|
|
|
9662
11957
|
exports.RichTextOptions = RichTextOptions;
|
|
9663
11958
|
exports.SCHEMA_SOURCE_FIXES = SCHEMA_SOURCE_FIXES;
|
|
9664
11959
|
exports.SELF_CHECK_VERSION = SELF_CHECK_VERSION;
|
|
11960
|
+
exports.SESSION_MEDIA_PREFIX = SESSION_MEDIA_PREFIX;
|
|
9665
11961
|
exports.SKETCHPAD_MAX_TEXT_LENGTH = SKETCHPAD_MAX_TEXT_LENGTH;
|
|
11962
|
+
exports.STAGED_SCHEMA_PREFIX = STAGED_SCHEMA_PREFIX;
|
|
9666
11963
|
exports.STUDIO_FIXES = STUDIO_FIXES;
|
|
9667
11964
|
exports.SerializedArraySchema = SerializedArraySchema;
|
|
9668
11965
|
exports.SerializedBooleanSchema = SerializedBooleanSchema;
|
|
@@ -9690,6 +11987,7 @@ exports.SerializedVideoSchema = SerializedVideoSchema;
|
|
|
9690
11987
|
exports.SharedValConfig = SharedValConfig;
|
|
9691
11988
|
exports.Sketchpad = Sketchpad;
|
|
9692
11989
|
exports.SketchpadSummary = SketchpadSummary;
|
|
11990
|
+
exports.VAL_CLIENT_HEADER = VAL_CLIENT_HEADER;
|
|
9693
11991
|
exports.VAL_ENABLE_COOKIE_NAME = VAL_ENABLE_COOKIE_NAME;
|
|
9694
11992
|
exports.VAL_GREEN_RAMP = VAL_GREEN_RAMP;
|
|
9695
11993
|
exports.VAL_SESSION_COOKIE = VAL_SESSION_COOKIE;
|
|
@@ -9702,17 +12000,24 @@ exports.VideoStreamOption = VideoStreamOption;
|
|
|
9702
12000
|
exports.WARNING_FIXES = WARNING_FIXES;
|
|
9703
12001
|
exports.WASM_ENCODED_MIME_TYPES = WASM_ENCODED_MIME_TYPES;
|
|
9704
12002
|
exports.accentRamp = accentRamp;
|
|
12003
|
+
exports.applyChain = applyChain;
|
|
12004
|
+
exports.applyPatchRecord = applyPatchRecord;
|
|
12005
|
+
exports.baseModuleContent = baseModuleContent;
|
|
12006
|
+
exports.baseVersionOf = baseVersionOf;
|
|
9705
12007
|
exports.buildDuplicatePatch = buildDuplicatePatch;
|
|
9706
12008
|
exports.buildEmptyAtPathPatch = buildEmptyAtPathPatch;
|
|
9707
12009
|
exports.buildFileRefValue = buildFileRefValue;
|
|
9708
12010
|
exports.buildImageGalleryPatch = buildImageGalleryPatch;
|
|
9709
12011
|
exports.buildRemoveImageGalleryEntryPatch = buildRemoveImageGalleryEntryPatch;
|
|
9710
12012
|
exports.buildSearchIndex = buildSearchIndex;
|
|
12013
|
+
exports.bundleTier = bundleTier;
|
|
9711
12014
|
exports.chainHeadOf = chainHeadOf;
|
|
12015
|
+
exports.chainOf = chainOf;
|
|
9712
12016
|
exports.checkCompatibility = checkCompatibility;
|
|
9713
12017
|
exports.checkSketch = checkSketch;
|
|
9714
12018
|
exports.childSource = childSource;
|
|
9715
12019
|
exports.chooseEncoded = chooseEncoded;
|
|
12020
|
+
exports.classifyJsonValuesOp = classifyJsonValuesOp;
|
|
9716
12021
|
exports.concatModulePath = concatModulePath;
|
|
9717
12022
|
exports.contrastRatio = contrastRatio;
|
|
9718
12023
|
exports.createRegExpFromPattern = createRegExpFromPattern;
|
|
@@ -9721,8 +12026,14 @@ exports.createValClient = createValClient;
|
|
|
9721
12026
|
exports.declaredLocales = declaredLocales;
|
|
9722
12027
|
exports.describeContainerAtPath = describeContainerAtPath;
|
|
9723
12028
|
exports.emptyOf = emptyOf;
|
|
12029
|
+
exports.engineWork = engineWork;
|
|
12030
|
+
exports.entryAddress = entryAddress;
|
|
12031
|
+
exports.entryState = entryState;
|
|
9724
12032
|
exports.enumLocaleTag = enumLocaleTag;
|
|
12033
|
+
exports.expandJsonValuesRootOp = expandJsonValuesRootOp;
|
|
12034
|
+
exports.expandJsonValuesRootOpForKey = expandJsonValuesRootOpForKey;
|
|
9725
12035
|
exports.explainIncompatible = explainIncompatible;
|
|
12036
|
+
exports.extendChain = extendChain;
|
|
9726
12037
|
exports.fallbackMimeType = fallbackMimeType;
|
|
9727
12038
|
exports.filterBlockingValidationErrors = filterBlockingValidationErrors;
|
|
9728
12039
|
exports.filterRoutesByPatterns = filterRoutesByPatterns;
|
|
@@ -9746,6 +12057,7 @@ exports.hasLocaleRouteParam = hasLocaleRouteParam;
|
|
|
9746
12057
|
exports.indexModule = indexModule;
|
|
9747
12058
|
exports.isInsidePatchSetPath = isInsidePatchSetPath;
|
|
9748
12059
|
exports.isJsonObject = isJsonObject;
|
|
12060
|
+
exports.isJsonValuesRootOp = isJsonValuesRootOp;
|
|
9749
12061
|
exports.isLocaleSchema = isLocaleSchema;
|
|
9750
12062
|
exports.isMimeTypeAccepted = isMimeTypeAccepted;
|
|
9751
12063
|
exports.isRemoteSchema = isRemoteSchema;
|
|
@@ -9764,17 +12076,22 @@ exports.localeOfModuleRoot = localeOfModuleRoot;
|
|
|
9764
12076
|
exports.localeOfRouteKey = localeOfRouteKey;
|
|
9765
12077
|
exports.localeParamOfRouteKey = localeParamOfRouteKey;
|
|
9766
12078
|
exports.localeScopeAt = localeScopeAt;
|
|
9767
|
-
exports.
|
|
12079
|
+
exports.memoryTier = memoryTier;
|
|
12080
|
+
exports.moduleKey = moduleKey;
|
|
9768
12081
|
exports.newestCommitSha = newestCommitSha;
|
|
12082
|
+
exports.parseModuleKey = parseModuleKey;
|
|
9769
12083
|
exports.parseSketch = parseSketch;
|
|
9770
12084
|
exports.partitionValidationErrors = partitionValidationErrors;
|
|
12085
|
+
exports.pathOfModuleKey = pathOfModuleKey;
|
|
9771
12086
|
exports.performSearch = performSearch;
|
|
9772
12087
|
exports.proposalCustomProperties = proposalCustomProperties;
|
|
12088
|
+
exports.rebaseContentOp = rebaseContentOp;
|
|
9773
12089
|
exports.relativeLuminance = relativeLuminance;
|
|
9774
12090
|
exports.relocateForLocale = relocateForLocale;
|
|
9775
12091
|
exports.remirrorToRichTextSource = remirrorToRichTextSource;
|
|
9776
12092
|
exports.removeModule = removeModule;
|
|
9777
12093
|
exports.resolveEncodeSettings = resolveEncodeSettings;
|
|
12094
|
+
exports.resolveEntry = resolveEntry;
|
|
9778
12095
|
exports.resolvePairGround = resolvePairGround;
|
|
9779
12096
|
exports.resolveSchemaSourceFixForError = resolveSchemaSourceFixForError;
|
|
9780
12097
|
exports.resolveSchemaSourceFixes = resolveSchemaSourceFixes;
|
|
@@ -9782,18 +12099,30 @@ exports.resolveSerializedSchemaAtPath = resolveSerializedSchemaAtPath;
|
|
|
9782
12099
|
exports.richTextImageSchema = richTextImageSchema;
|
|
9783
12100
|
exports.richTextToRemirror = richTextToRemirror;
|
|
9784
12101
|
exports.safeParsePatch = safeParsePatch;
|
|
12102
|
+
exports.sameEntryVersion = sameEntryVersion;
|
|
12103
|
+
exports.sameModuleVersion = sameModuleVersion;
|
|
9785
12104
|
exports.schemaErrorsOf = schemaErrorsOf;
|
|
9786
12105
|
exports.schemaTypesOfPath = schemaTypesOfPath;
|
|
12106
|
+
exports.schemaVersionOf = schemaVersionOf;
|
|
9787
12107
|
exports.selectionClosure = selectionClosure;
|
|
9788
12108
|
exports.selfCheck = selfCheck;
|
|
9789
12109
|
exports.shouldEncodeWithWasm = shouldEncodeWithWasm;
|
|
9790
12110
|
exports.sideFiles = sideFiles;
|
|
9791
12111
|
exports.sideSource = sideSource;
|
|
12112
|
+
exports.sketchFieldTexts = sketchFieldTexts;
|
|
12113
|
+
exports.sketchRoutes = sketchRoutes;
|
|
9792
12114
|
exports.sketchTitle = sketchTitle;
|
|
9793
12115
|
exports.sourcePathOfChild = sourcePathOfChild;
|
|
12116
|
+
exports.storeWork = storeWork;
|
|
9794
12117
|
exports.themeCustomProperties = themeCustomProperties;
|
|
12118
|
+
exports.tiered = tiered;
|
|
9795
12119
|
exports.toolNames = toolNames;
|
|
9796
12120
|
exports.traverseSchemaSource = traverseSchemaSource;
|
|
9797
12121
|
exports.urlOf = urlOf;
|
|
12122
|
+
exports.valClientHeaders = valClientHeaders;
|
|
12123
|
+
exports.valModulesEntryFile = valModulesEntryFile;
|
|
12124
|
+
exports.valModulesEntryPath = valModulesEntryPath;
|
|
9798
12125
|
exports.validateRoutePatterns = validateRoutePatterns;
|
|
12126
|
+
exports.versionHashCount = versionHashCount;
|
|
12127
|
+
exports.wholeEntryKey = wholeEntryKey;
|
|
9799
12128
|
exports.withExtension = withExtension;
|