@valbuild/shared 0.121.0 → 0.123.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 +178 -0
- package/dist/declarations/src/internal/ApiRoutes.d.ts +304 -41
- package/dist/declarations/src/internal/index.d.ts +7 -0
- package/dist/declarations/src/internal/media/encodeImageDecisions.d.ts +89 -0
- package/dist/declarations/src/internal/schema/compatibility.d.ts +62 -0
- package/dist/declarations/src/internal/search/getFilenameFromRef.d.ts +17 -0
- package/dist/declarations/src/internal/search/searchIndex.d.ts +76 -0
- package/dist/declarations/src/internal/search/sourcePath.d.ts +30 -0
- package/dist/declarations/src/internal/search/traverseSchemaSource.d.ts +20 -0
- package/dist/declarations/src/internal/zod/History.d.ts +257 -0
- package/dist/declarations/src/internal/zod/Patch.d.ts +7 -0
- package/dist/declarations/src/internal/zod/SerializedSchema.d.ts +2 -1
- package/internal/dist/valbuild-shared-internal.cjs.dev.js +1461 -124
- package/internal/dist/valbuild-shared-internal.cjs.prod.js +1461 -124
- package/internal/dist/valbuild-shared-internal.esm.js +1429 -127
- package/package.json +2 -1
|
@@ -6,6 +6,11 @@ var core = require('@valbuild/core');
|
|
|
6
6
|
var utils = require('../../dist/utils-32bcaca2.cjs.prod.js');
|
|
7
7
|
var zod = require('zod');
|
|
8
8
|
var zodValidationError = require('zod-validation-error');
|
|
9
|
+
var FlexSearch = require('flexsearch');
|
|
10
|
+
|
|
11
|
+
function _interopDefault (e) { return e && e.__esModule ? e : { 'default': e }; }
|
|
12
|
+
|
|
13
|
+
var FlexSearch__default = /*#__PURE__*/_interopDefault(FlexSearch);
|
|
9
14
|
|
|
10
15
|
function _toPrimitive(t, r) {
|
|
11
16
|
if ("object" != typeof t || !t) return t;
|
|
@@ -901,6 +906,12 @@ function _asyncToGenerator(n) {
|
|
|
901
906
|
};
|
|
902
907
|
}
|
|
903
908
|
|
|
909
|
+
/**
|
|
910
|
+
* JSON, recursively — the shape of a Source over the wire.
|
|
911
|
+
*
|
|
912
|
+
* Exported because history reads stored Sources back and needs the same
|
|
913
|
+
* definition; a second copy is a second thing to keep in step.
|
|
914
|
+
*/
|
|
904
915
|
var JSONValue = zod.z.lazy(function () {
|
|
905
916
|
return zod.z.union([zod.z.string(), zod.z.number(), zod.z["boolean"](), zod.z["null"](), zod.z.array(JSONValue), zod.z.record(zod.z.string(), JSONValue)]);
|
|
906
917
|
});
|
|
@@ -1279,7 +1290,26 @@ var SerializedRouteSchema = zod.z.object({
|
|
|
1279
1290
|
readonly: zod.z["boolean"]().optional(),
|
|
1280
1291
|
hidden: zod.z["boolean"]().optional()
|
|
1281
1292
|
});
|
|
1282
|
-
|
|
1293
|
+
|
|
1294
|
+
// A settings module, and each section inside one, serialize as this - the
|
|
1295
|
+
// shape is recursive because `items` holds sections which are themselves
|
|
1296
|
+
// settings schemas. `s.settings()` never writes render/preview/customValidate
|
|
1297
|
+
// (see core/src/schema/settings.ts), but they are accepted here because they
|
|
1298
|
+
// are part of the declared type.
|
|
1299
|
+
var SerializedSettingsSchema = zod.z.lazy(function () {
|
|
1300
|
+
return zod.z.object({
|
|
1301
|
+
type: zod.z.literal("settings"),
|
|
1302
|
+
render: FieldRender,
|
|
1303
|
+
preview: zod.z.literal(true).optional(),
|
|
1304
|
+
items: zod.z.record(zod.z.string(), SerializedSchema),
|
|
1305
|
+
opt: zod.z["boolean"](),
|
|
1306
|
+
customValidate: zod.z["boolean"]().optional(),
|
|
1307
|
+
readonly: zod.z["boolean"]().optional(),
|
|
1308
|
+
hidden: zod.z["boolean"]().optional(),
|
|
1309
|
+
description: zod.z.string().optional()
|
|
1310
|
+
});
|
|
1311
|
+
});
|
|
1312
|
+
var SerializedSchema = zod.z.union([SerializedStringSchema, SerializedLiteralSchema, SerializedBooleanSchema, SerializedNumberSchema, SerializedObjectSchema, SerializedArraySchema, SerializedUnionSchema, SerializedRichTextSchema, SerializedRecordSchema, SerializedKeyOfSchema, SerializedRouteSchema, SerializedFileSchema, SerializedDateSchema, SerializedDateTimeSchema, SerializedColorSchema, SerializedCodeSchema, SerializedSettingsSchema, SerializedImageSchema]);
|
|
1283
1313
|
|
|
1284
1314
|
var ValCommit = zod.z.object({
|
|
1285
1315
|
commitSha: zod.z.string(),
|
|
@@ -1291,6 +1321,125 @@ var ValCommit = zod.z.object({
|
|
|
1291
1321
|
commitMessage: zod.z.string().nullable()
|
|
1292
1322
|
});
|
|
1293
1323
|
|
|
1324
|
+
/**
|
|
1325
|
+
* The wire shapes for reading history.
|
|
1326
|
+
*
|
|
1327
|
+
* Here rather than in `@valbuild/server` because both sides of `/api/val` need
|
|
1328
|
+
* them: the server builds these and the Studio parses them, and a second
|
|
1329
|
+
* hand-written copy in the client is how the two drift.
|
|
1330
|
+
*
|
|
1331
|
+
* `HistoryError` is a closed union of everything that can go wrong
|
|
1332
|
+
* reconstructing a commit - see `packages/server/src/history/HistoryError.ts`
|
|
1333
|
+
* for what each one means. It travels as data rather than as a message because
|
|
1334
|
+
* the Studio decides what to OFFER from it: a schema mismatch means "cannot
|
|
1335
|
+
* restore this field", a missing source means "cannot restore this module at
|
|
1336
|
+
* all", and a rendered string cannot be told apart.
|
|
1337
|
+
*/
|
|
1338
|
+
var HistoryError = zod.z.discriminatedUnion("kind", [zod.z.object({
|
|
1339
|
+
kind: zod.z.literal("commit-not-found"),
|
|
1340
|
+
commitSha: zod.z.string()
|
|
1341
|
+
}), zod.z.object({
|
|
1342
|
+
kind: zod.z.literal("archive-unreadable"),
|
|
1343
|
+
commitSha: zod.z.string(),
|
|
1344
|
+
message: zod.z.string()
|
|
1345
|
+
}), zod.z.object({
|
|
1346
|
+
kind: zod.z.literal("source-unavailable"),
|
|
1347
|
+
moduleFilePath: zod.z.string()
|
|
1348
|
+
}), zod.z.object({
|
|
1349
|
+
kind: zod.z.literal("schema-unreadable"),
|
|
1350
|
+
moduleFilePath: zod.z.string(),
|
|
1351
|
+
message: zod.z.string()
|
|
1352
|
+
}), zod.z.object({
|
|
1353
|
+
kind: zod.z.literal("file-unavailable"),
|
|
1354
|
+
gitPath: zod.z.string(),
|
|
1355
|
+
message: zod.z.string()
|
|
1356
|
+
}), zod.z.object({
|
|
1357
|
+
kind: zod.z.literal("not-supported-in-fs-mode")
|
|
1358
|
+
}), zod.z.object({
|
|
1359
|
+
kind: zod.z.literal("transport"),
|
|
1360
|
+
message: zod.z.string()
|
|
1361
|
+
})]);
|
|
1362
|
+
var HistoricalCommit = zod.z.object({
|
|
1363
|
+
commitSha: zod.z.string(),
|
|
1364
|
+
parentCommitSha: zod.z.string(),
|
|
1365
|
+
clientCommitSha: zod.z.string(),
|
|
1366
|
+
branch: zod.z.string(),
|
|
1367
|
+
createdBranch: zod.z.string().nullable(),
|
|
1368
|
+
creator: zod.z.string().nullable(),
|
|
1369
|
+
message: zod.z.string().nullable(),
|
|
1370
|
+
createdAt: zod.z.string(),
|
|
1371
|
+
seqNum: zod.z.string(),
|
|
1372
|
+
patchCount: zod.z.number(),
|
|
1373
|
+
/** False for commits made before history was recorded. */
|
|
1374
|
+
hasArchive: zod.z["boolean"]()
|
|
1375
|
+
});
|
|
1376
|
+
var FileChange = zod.z.union([zod.z.literal("added"), zod.z.literal("modified"), zod.z.literal("deleted")]);
|
|
1377
|
+
|
|
1378
|
+
/**
|
|
1379
|
+
* A binary file the commit touched - named, with a URL, and nothing fetched.
|
|
1380
|
+
* Only an `<img src>` that mounts pays for the bytes.
|
|
1381
|
+
*/
|
|
1382
|
+
var BinaryFileRef = zod.z.object({
|
|
1383
|
+
gitPath: zod.z.string(),
|
|
1384
|
+
change: FileChange,
|
|
1385
|
+
remote: zod.z["boolean"](),
|
|
1386
|
+
url: zod.z.string()
|
|
1387
|
+
});
|
|
1388
|
+
var HistoricalModule = zod.z.object({
|
|
1389
|
+
/** The module's data at this commit. Null if deleted, or unreadable. */
|
|
1390
|
+
source: JSONValue.nullable(),
|
|
1391
|
+
/**
|
|
1392
|
+
* The schema at this commit, already validated against THIS Val.
|
|
1393
|
+
*
|
|
1394
|
+
* Null with a `schema-unreadable` failure means the stored schema came from a
|
|
1395
|
+
* Val whose schema format this one does not know — expected, not damage.
|
|
1396
|
+
*/
|
|
1397
|
+
schema: SerializedSchema.nullable(),
|
|
1398
|
+
patchIds: zod.z.array(PatchId),
|
|
1399
|
+
changedPaths: zod.z.array(zod.z.string()),
|
|
1400
|
+
failures: zod.z.array(HistoryError)
|
|
1401
|
+
});
|
|
1402
|
+
var HistoricalPatchSet = zod.z.object({
|
|
1403
|
+
commit: HistoricalCommit,
|
|
1404
|
+
modules: zod.z.record(zod.z.string(), HistoricalModule),
|
|
1405
|
+
patches: zod.z.array(zod.z.object({
|
|
1406
|
+
patchId: PatchId,
|
|
1407
|
+
moduleFilePath: zod.z.string(),
|
|
1408
|
+
patch: zod.z.unknown(),
|
|
1409
|
+
authorId: zod.z.string().nullable(),
|
|
1410
|
+
createdAt: zod.z.string(),
|
|
1411
|
+
baseSha: zod.z.string(),
|
|
1412
|
+
coreVersion: zod.z.string()
|
|
1413
|
+
})),
|
|
1414
|
+
jsonEntries: zod.z.record(zod.z.string(), zod.z.unknown()),
|
|
1415
|
+
binaryFiles: zod.z.array(BinaryFileRef),
|
|
1416
|
+
warnings: zod.z.array(HistoryError)
|
|
1417
|
+
});
|
|
1418
|
+
/**
|
|
1419
|
+
* Whether the historical value can be written back as the project stands.
|
|
1420
|
+
*
|
|
1421
|
+
* Computed in the Studio, not on the server: the verdict depends on the current
|
|
1422
|
+
* source and the current schema, both of which the client already holds, and it
|
|
1423
|
+
* runs against the same worker-thread validator that validates ordinary edits.
|
|
1424
|
+
* The types stay here because they are what a restore preview hands to the UI.
|
|
1425
|
+
*
|
|
1426
|
+
* `blocked` carries its reasons so a refusal can name the field and say why,
|
|
1427
|
+
* rather than being an unexplained disabled button.
|
|
1428
|
+
*/
|
|
1429
|
+
var RestoreVerdict = zod.z.union([zod.z.object({
|
|
1430
|
+
status: zod.z.literal("restorable")
|
|
1431
|
+
}), zod.z.object({
|
|
1432
|
+
status: zod.z.literal("blocked"),
|
|
1433
|
+
reasons: zod.z.array(HistoryError)
|
|
1434
|
+
})]);
|
|
1435
|
+
var ModuleComparison = zod.z.object({
|
|
1436
|
+
current: zod.z.unknown().nullable(),
|
|
1437
|
+
/** Paths where the commit's result and the current source differ: what a
|
|
1438
|
+
* restore would undo. */
|
|
1439
|
+
changedVsCurrent: zod.z.array(zod.z.string()),
|
|
1440
|
+
verdict: RestoreVerdict
|
|
1441
|
+
});
|
|
1442
|
+
|
|
1294
1443
|
var ModuleFilePath = zod.z.string().refine(function (_path) {
|
|
1295
1444
|
return true;
|
|
1296
1445
|
} // TODO: validation
|
|
@@ -1560,48 +1709,6 @@ var Api = {
|
|
|
1560
1709
|
})
|
|
1561
1710
|
}
|
|
1562
1711
|
},
|
|
1563
|
-
"/session": {
|
|
1564
|
-
GET: {
|
|
1565
|
-
req: {
|
|
1566
|
-
cookies: {
|
|
1567
|
-
val_session: zod.z.string().optional()
|
|
1568
|
-
}
|
|
1569
|
-
},
|
|
1570
|
-
res: zod.z.union([zod.z.object({
|
|
1571
|
-
status: zod.z.literal(200),
|
|
1572
|
-
json: zod.z.object({
|
|
1573
|
-
mode: zod.z.union([zod.z.literal("local"), zod.z.literal("proxy")]),
|
|
1574
|
-
enabled: zod.z["boolean"]()
|
|
1575
|
-
})
|
|
1576
|
-
}), zod.z.object({
|
|
1577
|
-
status: zod.z.union([
|
|
1578
|
-
// TODO: Remove the ones we don't need.
|
|
1579
|
-
zod.z.literal(400), zod.z.literal(401), zod.z.literal(403), zod.z.literal(404), zod.z.literal(500), zod.z.literal(501)]),
|
|
1580
|
-
json: zod.z.object({
|
|
1581
|
-
message: zod.z.string()
|
|
1582
|
-
})
|
|
1583
|
-
}), zod.z.object({
|
|
1584
|
-
status: zod.z.literal(401),
|
|
1585
|
-
json: zod.z.object({
|
|
1586
|
-
message: zod.z.string(),
|
|
1587
|
-
details: zod.z.union([zod.z.string(), zod.z.object({
|
|
1588
|
-
reason: zod.z.string()
|
|
1589
|
-
}), zod.z.object({
|
|
1590
|
-
sub: zod.z.string(),
|
|
1591
|
-
exp: zod.z.number(),
|
|
1592
|
-
token: zod.z.string(),
|
|
1593
|
-
org: zod.z.string(),
|
|
1594
|
-
project: zod.z.string()
|
|
1595
|
-
})])
|
|
1596
|
-
})
|
|
1597
|
-
}), zod.z.object({
|
|
1598
|
-
status: zod.z.literal(500),
|
|
1599
|
-
json: zod.z.object({
|
|
1600
|
-
message: zod.z.string()
|
|
1601
|
-
})
|
|
1602
|
-
})])
|
|
1603
|
-
}
|
|
1604
|
-
},
|
|
1605
1712
|
"/logout": {
|
|
1606
1713
|
GET: {
|
|
1607
1714
|
req: {
|
|
@@ -2393,6 +2500,144 @@ var Api = {
|
|
|
2393
2500
|
})])
|
|
2394
2501
|
}
|
|
2395
2502
|
},
|
|
2503
|
+
// #region history
|
|
2504
|
+
//
|
|
2505
|
+
// Reading the past. Split across three routes on purpose, because they cache
|
|
2506
|
+
// differently: a reconstructed commit cannot change and is immutable
|
|
2507
|
+
// forever, the comparison against current source changes with every edit, and
|
|
2508
|
+
// a file at a fixed commit is immutable too. Folding them together would mean
|
|
2509
|
+
// the cheapest thing dragging the most expensive one's cache headers down to
|
|
2510
|
+
// no-store.
|
|
2511
|
+
"/history/commits": {
|
|
2512
|
+
GET: {
|
|
2513
|
+
req: {
|
|
2514
|
+
query: {
|
|
2515
|
+
branch: onlyOneStringQueryParam,
|
|
2516
|
+
limit: onlyOneIntQueryParam.optional(),
|
|
2517
|
+
/** The previous page's `nextCursor`. */
|
|
2518
|
+
cursor: onlyOneStringQueryParam.optional()
|
|
2519
|
+
},
|
|
2520
|
+
cookies: {
|
|
2521
|
+
val_session: zod.z.string().optional()
|
|
2522
|
+
}
|
|
2523
|
+
},
|
|
2524
|
+
res: zod.z.union([unauthorizedResponse, notFoundResponse, zod.z.object({
|
|
2525
|
+
status: zod.z.literal(400),
|
|
2526
|
+
json: GenericError.and(zod.z.object({
|
|
2527
|
+
kind: zod.z.string().optional()
|
|
2528
|
+
}))
|
|
2529
|
+
}), zod.z.object({
|
|
2530
|
+
status: zod.z.literal(500),
|
|
2531
|
+
json: GenericError.and(zod.z.object({
|
|
2532
|
+
kind: zod.z.string().optional()
|
|
2533
|
+
}))
|
|
2534
|
+
}), zod.z.object({
|
|
2535
|
+
status: zod.z.literal(200),
|
|
2536
|
+
json: zod.z.object({
|
|
2537
|
+
commits: zod.z.array(HistoricalCommit),
|
|
2538
|
+
nextCursor: zod.z.string().nullable()
|
|
2539
|
+
})
|
|
2540
|
+
})])
|
|
2541
|
+
}
|
|
2542
|
+
},
|
|
2543
|
+
/**
|
|
2544
|
+
* One commit, reconstructed. IMMUTABLE for a given sha - it says nothing
|
|
2545
|
+
* about the current source or schema, which is what lets it be cached
|
|
2546
|
+
* forever and what makes comparing many commits cheap.
|
|
2547
|
+
*/
|
|
2548
|
+
"/history/commit": {
|
|
2549
|
+
GET: {
|
|
2550
|
+
req: {
|
|
2551
|
+
query: {
|
|
2552
|
+
commit_sha: onlyOneStringQueryParam
|
|
2553
|
+
},
|
|
2554
|
+
cookies: {
|
|
2555
|
+
val_session: zod.z.string().optional()
|
|
2556
|
+
}
|
|
2557
|
+
},
|
|
2558
|
+
res: zod.z.union([unauthorizedResponse, notFoundResponse, zod.z.object({
|
|
2559
|
+
status: zod.z.literal(400),
|
|
2560
|
+
json: GenericError
|
|
2561
|
+
}), zod.z.object({
|
|
2562
|
+
status: zod.z.literal(500),
|
|
2563
|
+
json: GenericError.and(zod.z.object({
|
|
2564
|
+
kind: zod.z.string().optional()
|
|
2565
|
+
}))
|
|
2566
|
+
}), zod.z.object({
|
|
2567
|
+
status: zod.z.literal(200),
|
|
2568
|
+
json: HistoricalPatchSet
|
|
2569
|
+
})])
|
|
2570
|
+
}
|
|
2571
|
+
},
|
|
2572
|
+
/**
|
|
2573
|
+
* ONE module, as of a commit — including a commit that did not change it.
|
|
2574
|
+
*
|
|
2575
|
+
* `/history/commit` covers what a commit changed, which is the narrow case:
|
|
2576
|
+
* navigate the history pane to anything else and there would be nothing to
|
|
2577
|
+
* show. This answers "how did this module look at that point", which is the
|
|
2578
|
+
* question someone comparing two panes is actually asking. Immutable for the
|
|
2579
|
+
* same reason: what a commit left behind cannot change.
|
|
2580
|
+
*/
|
|
2581
|
+
"/history/module": {
|
|
2582
|
+
GET: {
|
|
2583
|
+
req: {
|
|
2584
|
+
query: {
|
|
2585
|
+
commit_sha: onlyOneStringQueryParam,
|
|
2586
|
+
module_file_path: onlyOneStringQueryParam
|
|
2587
|
+
},
|
|
2588
|
+
cookies: {
|
|
2589
|
+
val_session: zod.z.string().optional()
|
|
2590
|
+
}
|
|
2591
|
+
},
|
|
2592
|
+
res: zod.z.union([unauthorizedResponse, notFoundResponse, zod.z.object({
|
|
2593
|
+
status: zod.z.literal(400),
|
|
2594
|
+
json: GenericError
|
|
2595
|
+
}), zod.z.object({
|
|
2596
|
+
status: zod.z.literal(500),
|
|
2597
|
+
json: GenericError.and(zod.z.object({
|
|
2598
|
+
kind: zod.z.string().optional()
|
|
2599
|
+
}))
|
|
2600
|
+
}), zod.z.object({
|
|
2601
|
+
status: zod.z.literal(200),
|
|
2602
|
+
json: zod.z.object({
|
|
2603
|
+
moduleFilePath: zod.z.string(),
|
|
2604
|
+
/**
|
|
2605
|
+
* Null when history has no record of this module at or before the
|
|
2606
|
+
* commit — it was last edited before recording started, or never.
|
|
2607
|
+
* Distinct from a module the commit deleted.
|
|
2608
|
+
*/
|
|
2609
|
+
module: HistoricalModule.nullable()
|
|
2610
|
+
})
|
|
2611
|
+
})])
|
|
2612
|
+
}
|
|
2613
|
+
},
|
|
2614
|
+
/**
|
|
2615
|
+
* One file as it was at one commit.
|
|
2616
|
+
*
|
|
2617
|
+
* The lazy half: `/history/commit` NAMES a commit's binary files and this is
|
|
2618
|
+
* where their bytes come from, so nothing is fetched until an `<img>`
|
|
2619
|
+
* actually mounts. A file at a fixed commit cannot change, so the response is
|
|
2620
|
+
* immutable and the browser's own cache handles flipping between commits.
|
|
2621
|
+
*/
|
|
2622
|
+
"/history/files": {
|
|
2623
|
+
GET: {
|
|
2624
|
+
req: {
|
|
2625
|
+
query: {
|
|
2626
|
+
commit_sha: onlyOneStringQueryParam,
|
|
2627
|
+
path: onlyOneStringQueryParam,
|
|
2628
|
+
remote: onlyOneStringQueryParam.optional()
|
|
2629
|
+
}
|
|
2630
|
+
},
|
|
2631
|
+
res: zod.z.union([unauthorizedResponse, notFoundResponse, zod.z.object({
|
|
2632
|
+
status: zod.z.literal(400),
|
|
2633
|
+
json: GenericError
|
|
2634
|
+
}), zod.z.object({
|
|
2635
|
+
status: zod.z.literal(200),
|
|
2636
|
+
body: zod.z["instanceof"](ReadableStream)
|
|
2637
|
+
})])
|
|
2638
|
+
}
|
|
2639
|
+
},
|
|
2640
|
+
// #endregion history
|
|
2396
2641
|
"/files": {
|
|
2397
2642
|
GET: {
|
|
2398
2643
|
req: {
|
|
@@ -3030,96 +3275,444 @@ var urlOf = function urlOf() {
|
|
|
3030
3275
|
};
|
|
3031
3276
|
|
|
3032
3277
|
/**
|
|
3033
|
-
*
|
|
3034
|
-
*
|
|
3035
|
-
* This is the **publish head**: unlike `baseSha`, which only moves once a
|
|
3036
|
-
* deployment lands, it moves the instant somebody publishes. That makes it the
|
|
3037
|
-
* one thing a client can carry to `/save` to say "this is the world I decided
|
|
3038
|
-
* against" — and the only way the server can tell that somebody else has
|
|
3039
|
-
* published since the review screen was read.
|
|
3278
|
+
* Can this value, taken from one schema, be written into another?
|
|
3040
3279
|
*
|
|
3041
|
-
*
|
|
3042
|
-
*
|
|
3043
|
-
*
|
|
3280
|
+
* The question restore asks, and it is NOT the question validation asks.
|
|
3281
|
+
* Validation checks a value against the schema it belongs to. This compares two
|
|
3282
|
+
* schemas — the one a value was stored under and the one it is being written
|
|
3283
|
+
* into — because a restore moves a value across a schema change, and possibly
|
|
3284
|
+
* across paths.
|
|
3044
3285
|
*
|
|
3045
|
-
*
|
|
3046
|
-
*
|
|
3047
|
-
*
|
|
3048
|
-
*
|
|
3286
|
+
* Three answers, not two. `unknown` is where the comparison genuinely cannot
|
|
3287
|
+
* decide, and it exists so that an editor is told "we are not sure" rather than
|
|
3288
|
+
* being given a confident answer we made up. Treating unknown as compatible
|
|
3289
|
+
* writes values that break; treating it as incompatible blocks restores that
|
|
3290
|
+
* would have been fine.
|
|
3049
3291
|
*/
|
|
3050
|
-
function newestCommitSha(commits) {
|
|
3051
|
-
var _newest$commitSha, _newest;
|
|
3052
|
-
var newest = null;
|
|
3053
|
-
var _iterator = _createForOfIteratorHelper(commits !== null && commits !== void 0 ? commits : []),
|
|
3054
|
-
_step;
|
|
3055
|
-
try {
|
|
3056
|
-
for (_iterator.s(); !(_step = _iterator.n()).done;) {
|
|
3057
|
-
var commit = _step.value;
|
|
3058
|
-
if (newest === null || commit.createdAt > newest.createdAt) {
|
|
3059
|
-
newest = commit;
|
|
3060
|
-
}
|
|
3061
|
-
}
|
|
3062
|
-
} catch (err) {
|
|
3063
|
-
_iterator.e(err);
|
|
3064
|
-
} finally {
|
|
3065
|
-
_iterator.f();
|
|
3066
|
-
}
|
|
3067
|
-
return (_newest$commitSha = (_newest = newest) === null || _newest === void 0 ? void 0 : _newest.commitSha) !== null && _newest$commitSha !== void 0 ? _newest$commitSha : null;
|
|
3068
|
-
}
|
|
3069
3292
|
|
|
3070
|
-
var
|
|
3071
|
-
|
|
3072
|
-
|
|
3293
|
+
var compatible = {
|
|
3294
|
+
status: "compatible"
|
|
3295
|
+
};
|
|
3073
3296
|
|
|
3074
|
-
|
|
3075
|
-
|
|
3076
|
-
|
|
3077
|
-
|
|
3078
|
-
|
|
3079
|
-
|
|
3080
|
-
|
|
3081
|
-
|
|
3082
|
-
|
|
3083
|
-
|
|
3084
|
-
|
|
3085
|
-
|
|
3086
|
-
}
|
|
3087
|
-
}
|
|
3297
|
+
/**
|
|
3298
|
+
* `Array.isArray` cannot narrow `JsonArray` out of `Json`, because it is
|
|
3299
|
+
* `readonly Json[]` — so the check that reads naturally does not type. This
|
|
3300
|
+
* predicate is that check, said once.
|
|
3301
|
+
*/
|
|
3302
|
+
function isJsonObject(value) {
|
|
3303
|
+
return _typeof(value) === "object" && value !== null && !Array.isArray(value);
|
|
3304
|
+
}
|
|
3305
|
+
function incompatible(reason) {
|
|
3306
|
+
return {
|
|
3307
|
+
status: "incompatible",
|
|
3308
|
+
reason: reason
|
|
3309
|
+
};
|
|
3310
|
+
}
|
|
3088
3311
|
|
|
3089
3312
|
/**
|
|
3090
|
-
*
|
|
3313
|
+
* "an object", "a string".
|
|
3314
|
+
*
|
|
3315
|
+
* Schema type names are data — `object`, `array`, `image` — so an interpolated
|
|
3316
|
+
* "a" is wrong for three of them, in a sentence shown to editors the moment a
|
|
3317
|
+
* restore is refused.
|
|
3091
3318
|
*/
|
|
3319
|
+
function withArticle(type) {
|
|
3320
|
+
return /^[aeiou]/i.test(type) ? "an ".concat(type) : "a ".concat(type);
|
|
3321
|
+
}
|
|
3092
3322
|
|
|
3093
|
-
|
|
3094
|
-
|
|
3095
|
-
|
|
3096
|
-
|
|
3097
|
-
|
|
3098
|
-
|
|
3099
|
-
|
|
3323
|
+
/**
|
|
3324
|
+
* A sentence an editor can act on, for the popup that explains a refusal.
|
|
3325
|
+
*
|
|
3326
|
+
* Kept beside the reasons rather than in the UI so that adding a reason without
|
|
3327
|
+
* wording for it is a type error rather than a blank dialog.
|
|
3328
|
+
*/
|
|
3329
|
+
function explainIncompatible(reason) {
|
|
3330
|
+
switch (reason.kind) {
|
|
3331
|
+
case "type-changed":
|
|
3332
|
+
return "This was ".concat(withArticle(reason.from), " and the field here is ").concat(withArticle(reason.to), ".");
|
|
3333
|
+
case "no-matching-variant":
|
|
3334
|
+
return "This does not match any of the shapes allowed here: ".concat(reason.variants.join(", "), ".");
|
|
3335
|
+
case "unknown-field":
|
|
3336
|
+
return "The old value has ".concat(reason.keys.length === 1 ? "a field" : "fields", " that no longer exist here: ").concat(reason.keys.join(", "), ".");
|
|
3337
|
+
case "missing-required":
|
|
3338
|
+
return "The field here needs ".concat(reason.keys.join(", "), ", which the old value does not have.");
|
|
3339
|
+
case "not-optional":
|
|
3340
|
+
return "The old value was empty, and this field cannot be empty.";
|
|
3341
|
+
case "literal-changed":
|
|
3342
|
+
return "This has to be exactly \"".concat(reason.to, "\", and the old value was \"").concat(reason.from, "\".");
|
|
3343
|
+
case "different-module":
|
|
3344
|
+
return "This points into ".concat(reason.to, ", and the old value pointed into ").concat(reason.from, ".");
|
|
3345
|
+
}
|
|
3346
|
+
}
|
|
3100
3347
|
|
|
3101
|
-
|
|
3102
|
-
|
|
3103
|
-
|
|
3104
|
-
|
|
3105
|
-
|
|
3106
|
-
|
|
3107
|
-
|
|
3108
|
-
|
|
3109
|
-
|
|
3110
|
-
|
|
3111
|
-
|
|
3112
|
-
|
|
3113
|
-
|
|
3114
|
-
|
|
3115
|
-
|
|
3116
|
-
|
|
3117
|
-
|
|
3118
|
-
|
|
3119
|
-
|
|
3120
|
-
|
|
3121
|
-
|
|
3122
|
-
|
|
3348
|
+
/**
|
|
3349
|
+
* The variant of a union that a value actually is.
|
|
3350
|
+
*
|
|
3351
|
+
* `undefined` when the value does not identify itself — which is a real answer,
|
|
3352
|
+
* not a failure: it is what makes the caller return `unknown` rather than guess.
|
|
3353
|
+
*/
|
|
3354
|
+
function variantOf(schema, value) {
|
|
3355
|
+
if (schema.type !== "union") {
|
|
3356
|
+
return undefined;
|
|
3357
|
+
}
|
|
3358
|
+
// String union: the value IS the discriminator.
|
|
3359
|
+
if (typeof schema.key !== "string") {
|
|
3360
|
+
if (typeof value !== "string") {
|
|
3361
|
+
return undefined;
|
|
3362
|
+
}
|
|
3363
|
+
return schema.items.find(function (item) {
|
|
3364
|
+
return item.type === "literal" && item.value === value;
|
|
3365
|
+
});
|
|
3366
|
+
}
|
|
3367
|
+
// Object union: the discriminator is a literal at `key`.
|
|
3368
|
+
var discriminator = schema.key;
|
|
3369
|
+
if (!isJsonObject(value)) {
|
|
3370
|
+
return undefined;
|
|
3371
|
+
}
|
|
3372
|
+
var tag = value[discriminator];
|
|
3373
|
+
if (typeof tag !== "string") {
|
|
3374
|
+
return undefined;
|
|
3375
|
+
}
|
|
3376
|
+
return schema.items.find(function (item) {
|
|
3377
|
+
if (item.type !== "object") {
|
|
3378
|
+
return false;
|
|
3379
|
+
}
|
|
3380
|
+
var keySchema = item.items[discriminator];
|
|
3381
|
+
return (keySchema === null || keySchema === void 0 ? void 0 : keySchema.type) === "literal" && keySchema.value === tag;
|
|
3382
|
+
});
|
|
3383
|
+
}
|
|
3384
|
+
function describeVariant(schema, key) {
|
|
3385
|
+
if (schema.type === "literal") {
|
|
3386
|
+
return schema.value;
|
|
3387
|
+
}
|
|
3388
|
+
if (schema.type === "object") {
|
|
3389
|
+
var tag = schema.items[key];
|
|
3390
|
+
if ((tag === null || tag === void 0 ? void 0 : tag.type) === "literal") {
|
|
3391
|
+
return tag.value;
|
|
3392
|
+
}
|
|
3393
|
+
}
|
|
3394
|
+
return schema.type;
|
|
3395
|
+
}
|
|
3396
|
+
function checkCompatibility(from, to) {
|
|
3397
|
+
// Emptiness first: a null that lands in a field which cannot be empty is a
|
|
3398
|
+
// break regardless of what the two schemas otherwise say.
|
|
3399
|
+
if (from.value === null) {
|
|
3400
|
+
return to.schema.opt ? compatible : incompatible({
|
|
3401
|
+
kind: "not-optional"
|
|
3402
|
+
});
|
|
3403
|
+
}
|
|
3404
|
+
|
|
3405
|
+
// A changed union is not itself disqualifying. What matters is whether the
|
|
3406
|
+
// variant being restored still has somewhere to go — comparing the unions
|
|
3407
|
+
// wholesale would refuse a restore that is perfectly safe.
|
|
3408
|
+
if (to.schema.type === "union") {
|
|
3409
|
+
var key = typeof to.schema.key === "string" ? to.schema.key : "";
|
|
3410
|
+
var _iterator = _createForOfIteratorHelper(to.schema.items),
|
|
3411
|
+
_step;
|
|
3412
|
+
try {
|
|
3413
|
+
for (_iterator.s(); !(_step = _iterator.n()).done;) {
|
|
3414
|
+
var variant = _step.value;
|
|
3415
|
+
if (checkCompatibility(from, {
|
|
3416
|
+
schema: variant
|
|
3417
|
+
}).status === "compatible") {
|
|
3418
|
+
return compatible;
|
|
3419
|
+
}
|
|
3420
|
+
}
|
|
3421
|
+
} catch (err) {
|
|
3422
|
+
_iterator.e(err);
|
|
3423
|
+
} finally {
|
|
3424
|
+
_iterator.f();
|
|
3425
|
+
}
|
|
3426
|
+
return incompatible({
|
|
3427
|
+
kind: "no-matching-variant",
|
|
3428
|
+
variants: to.schema.items.map(function (item) {
|
|
3429
|
+
return describeVariant(item, key);
|
|
3430
|
+
})
|
|
3431
|
+
});
|
|
3432
|
+
}
|
|
3433
|
+
|
|
3434
|
+
// Coming FROM a union into something that is not one: only the variant this
|
|
3435
|
+
// value actually is matters, so narrow before comparing.
|
|
3436
|
+
if (from.schema.type === "union") {
|
|
3437
|
+
var _variant = variantOf(from.schema, from.value);
|
|
3438
|
+
if (_variant === undefined) {
|
|
3439
|
+
return {
|
|
3440
|
+
status: "unknown",
|
|
3441
|
+
message: "The old value does not say which shape it is, so we cannot tell whether it fits here."
|
|
3442
|
+
};
|
|
3443
|
+
}
|
|
3444
|
+
return checkCompatibility({
|
|
3445
|
+
schema: _variant,
|
|
3446
|
+
value: from.value
|
|
3447
|
+
}, to);
|
|
3448
|
+
}
|
|
3449
|
+
if (from.schema.type !== to.schema.type) {
|
|
3450
|
+
return incompatible({
|
|
3451
|
+
kind: "type-changed",
|
|
3452
|
+
from: from.schema.type,
|
|
3453
|
+
to: to.schema.type
|
|
3454
|
+
});
|
|
3455
|
+
}
|
|
3456
|
+
switch (to.schema.type) {
|
|
3457
|
+
case "literal":
|
|
3458
|
+
{
|
|
3459
|
+
var fromValue = from.schema.type === "literal" ? from.schema.value : String(from.value);
|
|
3460
|
+
return fromValue === to.schema.value ? compatible : incompatible({
|
|
3461
|
+
kind: "literal-changed",
|
|
3462
|
+
from: fromValue,
|
|
3463
|
+
to: to.schema.value
|
|
3464
|
+
});
|
|
3465
|
+
}
|
|
3466
|
+
case "object":
|
|
3467
|
+
{
|
|
3468
|
+
if (from.schema.type !== "object" || !isJsonObject(from.value)) {
|
|
3469
|
+
return {
|
|
3470
|
+
status: "unknown",
|
|
3471
|
+
message: "The old value is not shaped like an object."
|
|
3472
|
+
};
|
|
3473
|
+
}
|
|
3474
|
+
var _fromValue = from.value;
|
|
3475
|
+
var target = to.schema.items;
|
|
3476
|
+
var source = from.schema.items;
|
|
3477
|
+
|
|
3478
|
+
// Keys the old value carries that the field no longer has. Writing them
|
|
3479
|
+
// would put data into a schema with nowhere to hold it.
|
|
3480
|
+
var extra = Object.keys(source).filter(function (key) {
|
|
3481
|
+
return !(key in target);
|
|
3482
|
+
});
|
|
3483
|
+
if (extra.length > 0) {
|
|
3484
|
+
return incompatible({
|
|
3485
|
+
kind: "unknown-field",
|
|
3486
|
+
keys: extra
|
|
3487
|
+
});
|
|
3488
|
+
}
|
|
3489
|
+
|
|
3490
|
+
// Keys the field requires that the old value cannot supply.
|
|
3491
|
+
var missing = Object.keys(target).filter(function (key) {
|
|
3492
|
+
return !target[key].opt && !(key in source);
|
|
3493
|
+
});
|
|
3494
|
+
if (missing.length > 0) {
|
|
3495
|
+
return incompatible({
|
|
3496
|
+
kind: "missing-required",
|
|
3497
|
+
keys: missing
|
|
3498
|
+
});
|
|
3499
|
+
}
|
|
3500
|
+
var unknown = null;
|
|
3501
|
+
for (var _i = 0, _Object$entries = Object.entries(target); _i < _Object$entries.length; _i++) {
|
|
3502
|
+
var _fromValue$_key;
|
|
3503
|
+
var _Object$entries$_i = _slicedToArray(_Object$entries[_i], 2),
|
|
3504
|
+
_key = _Object$entries$_i[0],
|
|
3505
|
+
targetItem = _Object$entries$_i[1];
|
|
3506
|
+
var sourceItem = source[_key];
|
|
3507
|
+
if (sourceItem === undefined) {
|
|
3508
|
+
continue; // optional and absent — already allowed above
|
|
3509
|
+
}
|
|
3510
|
+
var res = checkCompatibility({
|
|
3511
|
+
schema: sourceItem,
|
|
3512
|
+
value: (_fromValue$_key = _fromValue[_key]) !== null && _fromValue$_key !== void 0 ? _fromValue$_key : null
|
|
3513
|
+
}, {
|
|
3514
|
+
schema: targetItem
|
|
3515
|
+
});
|
|
3516
|
+
if (res.status === "incompatible") {
|
|
3517
|
+
return res;
|
|
3518
|
+
}
|
|
3519
|
+
if (res.status === "unknown" && unknown === null) {
|
|
3520
|
+
unknown = res;
|
|
3521
|
+
}
|
|
3522
|
+
}
|
|
3523
|
+
return unknown !== null && unknown !== void 0 ? unknown : compatible;
|
|
3524
|
+
}
|
|
3525
|
+
case "array":
|
|
3526
|
+
{
|
|
3527
|
+
if (from.schema.type !== "array" || !Array.isArray(from.value)) {
|
|
3528
|
+
return {
|
|
3529
|
+
status: "unknown",
|
|
3530
|
+
message: "The old value is not a list."
|
|
3531
|
+
};
|
|
3532
|
+
}
|
|
3533
|
+
// An empty list has nothing to probe the item schema with. The item
|
|
3534
|
+
// TYPES still have to agree, and that much can be decided without a
|
|
3535
|
+
// value - so decide it rather than passing an empty list as proof.
|
|
3536
|
+
if (from.value.length === 0) {
|
|
3537
|
+
return from.schema.item.type === to.schema.item.type ? compatible : incompatible({
|
|
3538
|
+
kind: "type-changed",
|
|
3539
|
+
from: from.schema.item.type,
|
|
3540
|
+
to: to.schema.item.type
|
|
3541
|
+
});
|
|
3542
|
+
}
|
|
3543
|
+
var _unknown = null;
|
|
3544
|
+
var _iterator2 = _createForOfIteratorHelper(from.value),
|
|
3545
|
+
_step2;
|
|
3546
|
+
try {
|
|
3547
|
+
for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
|
|
3548
|
+
var item = _step2.value;
|
|
3549
|
+
var _res = checkCompatibility({
|
|
3550
|
+
schema: from.schema.item,
|
|
3551
|
+
value: item
|
|
3552
|
+
}, {
|
|
3553
|
+
schema: to.schema.item
|
|
3554
|
+
});
|
|
3555
|
+
if (_res.status === "incompatible") {
|
|
3556
|
+
return _res;
|
|
3557
|
+
}
|
|
3558
|
+
if (_res.status === "unknown" && _unknown === null) {
|
|
3559
|
+
_unknown = _res;
|
|
3560
|
+
}
|
|
3561
|
+
}
|
|
3562
|
+
} catch (err) {
|
|
3563
|
+
_iterator2.e(err);
|
|
3564
|
+
} finally {
|
|
3565
|
+
_iterator2.f();
|
|
3566
|
+
}
|
|
3567
|
+
return _unknown !== null && _unknown !== void 0 ? _unknown : compatible;
|
|
3568
|
+
}
|
|
3569
|
+
case "record":
|
|
3570
|
+
{
|
|
3571
|
+
if (from.schema.type !== "record" || !isJsonObject(from.value)) {
|
|
3572
|
+
return {
|
|
3573
|
+
status: "unknown",
|
|
3574
|
+
message: "The old value is not a record."
|
|
3575
|
+
};
|
|
3576
|
+
}
|
|
3577
|
+
var _unknown2 = null;
|
|
3578
|
+
for (var _i2 = 0, _Object$values = Object.values(from.value); _i2 < _Object$values.length; _i2++) {
|
|
3579
|
+
var entry = _Object$values[_i2];
|
|
3580
|
+
var _res2 = checkCompatibility({
|
|
3581
|
+
schema: from.schema.item,
|
|
3582
|
+
value: entry !== null && entry !== void 0 ? entry : null
|
|
3583
|
+
}, {
|
|
3584
|
+
schema: to.schema.item
|
|
3585
|
+
});
|
|
3586
|
+
if (_res2.status === "incompatible") {
|
|
3587
|
+
return _res2;
|
|
3588
|
+
}
|
|
3589
|
+
if (_res2.status === "unknown" && _unknown2 === null) {
|
|
3590
|
+
_unknown2 = _res2;
|
|
3591
|
+
}
|
|
3592
|
+
}
|
|
3593
|
+
return _unknown2 !== null && _unknown2 !== void 0 ? _unknown2 : compatible;
|
|
3594
|
+
}
|
|
3595
|
+
case "keyOf":
|
|
3596
|
+
{
|
|
3597
|
+
// A key is only meaningful against the module it indexes.
|
|
3598
|
+
if (from.schema.type !== "keyOf") {
|
|
3599
|
+
return compatible;
|
|
3600
|
+
}
|
|
3601
|
+
return from.schema.path === to.schema.path ? compatible : incompatible({
|
|
3602
|
+
kind: "different-module",
|
|
3603
|
+
from: from.schema.path,
|
|
3604
|
+
to: to.schema.path
|
|
3605
|
+
});
|
|
3606
|
+
}
|
|
3607
|
+
case "richtext":
|
|
3608
|
+
{
|
|
3609
|
+
// The options decide which nodes are legal, and comparing them properly
|
|
3610
|
+
// means comparing node trees against them. Not yet done, and saying so is
|
|
3611
|
+
// better than a confident answer we cannot back.
|
|
3612
|
+
return {
|
|
3613
|
+
status: "unknown",
|
|
3614
|
+
message: "Rich text can be restored, but we cannot yet confirm every mark and block is still allowed here."
|
|
3615
|
+
};
|
|
3616
|
+
}
|
|
3617
|
+
default:
|
|
3618
|
+
// Primitives and media: same type is the whole test. Constraints
|
|
3619
|
+
// (maxLength, accept, …) are validated on write, which reports them
|
|
3620
|
+
// better than a schema comparison could.
|
|
3621
|
+
return compatible;
|
|
3622
|
+
}
|
|
3623
|
+
}
|
|
3624
|
+
|
|
3625
|
+
/**
|
|
3626
|
+
* The newest commit the client has been told about, or `null` if none.
|
|
3627
|
+
*
|
|
3628
|
+
* This is the **publish head**: unlike `baseSha`, which only moves once a
|
|
3629
|
+
* deployment lands, it moves the instant somebody publishes. That makes it the
|
|
3630
|
+
* one thing a client can carry to `/save` to say "this is the world I decided
|
|
3631
|
+
* against" — and the only way the server can tell that somebody else has
|
|
3632
|
+
* published since the review screen was read.
|
|
3633
|
+
*
|
|
3634
|
+
* In `shared` because both sides need the SAME answer: the client names a head
|
|
3635
|
+
* and the server compares against one, so two implementations of "newest" is two
|
|
3636
|
+
* ways to disagree about whether a publish is stale.
|
|
3637
|
+
*
|
|
3638
|
+
* By `createdAt` rather than by position, because the list's order is the
|
|
3639
|
+
* content API's business and nothing here should depend on it. Ties keep the
|
|
3640
|
+
* first seen, which is arbitrary and does not matter: two commits sharing a
|
|
3641
|
+
* timestamp are equally "since", and either answer refuses the same publishes.
|
|
3642
|
+
*/
|
|
3643
|
+
function newestCommitSha(commits) {
|
|
3644
|
+
var _newest$commitSha, _newest;
|
|
3645
|
+
var newest = null;
|
|
3646
|
+
var _iterator = _createForOfIteratorHelper(commits !== null && commits !== void 0 ? commits : []),
|
|
3647
|
+
_step;
|
|
3648
|
+
try {
|
|
3649
|
+
for (_iterator.s(); !(_step = _iterator.n()).done;) {
|
|
3650
|
+
var commit = _step.value;
|
|
3651
|
+
if (newest === null || commit.createdAt > newest.createdAt) {
|
|
3652
|
+
newest = commit;
|
|
3653
|
+
}
|
|
3654
|
+
}
|
|
3655
|
+
} catch (err) {
|
|
3656
|
+
_iterator.e(err);
|
|
3657
|
+
} finally {
|
|
3658
|
+
_iterator.f();
|
|
3659
|
+
}
|
|
3660
|
+
return (_newest$commitSha = (_newest = newest) === null || _newest === void 0 ? void 0 : _newest.commitSha) !== null && _newest$commitSha !== void 0 ? _newest$commitSha : null;
|
|
3661
|
+
}
|
|
3662
|
+
|
|
3663
|
+
var VAL_CONFIG_SESSION_STORAGE_KEY = "val-config";
|
|
3664
|
+
var VAL_THEME_SESSION_STORAGE_KEY = "val-theme";
|
|
3665
|
+
var VAL_AI_SESSION_STORAGE_KEY = "val-ai-session";
|
|
3666
|
+
|
|
3667
|
+
var SharedValConfig = zod.z.object({
|
|
3668
|
+
contentHostUrl: zod.z.string().optional(),
|
|
3669
|
+
appHostUrl: zod.z.string().optional(),
|
|
3670
|
+
project: zod.z.string().optional(),
|
|
3671
|
+
root: zod.z.string().optional(),
|
|
3672
|
+
gitCommit: zod.z.string().optional(),
|
|
3673
|
+
gitBranch: zod.z.string().optional(),
|
|
3674
|
+
defaultTheme: zod.z["enum"](["dark", "light"]).optional(),
|
|
3675
|
+
ai: zod.z.object({
|
|
3676
|
+
commitMessages: zod.z.object({
|
|
3677
|
+
disabled: zod.z["boolean"]().optional()
|
|
3678
|
+
}).optional()
|
|
3679
|
+
}).optional()
|
|
3680
|
+
});
|
|
3681
|
+
|
|
3682
|
+
/**
|
|
3683
|
+
* Config that is shared between the client and server.
|
|
3684
|
+
*/
|
|
3685
|
+
|
|
3686
|
+
var ValDeployment = zod.z.object({
|
|
3687
|
+
deploymentId: zod.z.string(),
|
|
3688
|
+
deploymentState: zod.z.string(),
|
|
3689
|
+
commitSha: zod.z.string(),
|
|
3690
|
+
updatedAt: zod.z.string(),
|
|
3691
|
+
createdAt: zod.z.string()
|
|
3692
|
+
});
|
|
3693
|
+
|
|
3694
|
+
// Strictly speaking this should be in the next package but it's shared, because we want to use it in the ui package. We want to resolve that somehow
|
|
3695
|
+
function getNextAppRouterSitemapTree(srcFolder, paths) {
|
|
3696
|
+
var root = {
|
|
3697
|
+
type: "node",
|
|
3698
|
+
name: "/",
|
|
3699
|
+
children: []
|
|
3700
|
+
};
|
|
3701
|
+
var _iterator = _createForOfIteratorHelper(paths),
|
|
3702
|
+
_step;
|
|
3703
|
+
try {
|
|
3704
|
+
var _loop = function _loop() {
|
|
3705
|
+
var path = _step.value;
|
|
3706
|
+
var urlPath = path.urlPath,
|
|
3707
|
+
moduleFilePathS = path.moduleFilePath;
|
|
3708
|
+
var moduleFilePath = moduleFilePathS;
|
|
3709
|
+
if (!urlPath.startsWith("/")) {
|
|
3710
|
+
console.error("urlPath must start with /: ".concat(urlPath));
|
|
3711
|
+
return 0; // continue
|
|
3712
|
+
}
|
|
3713
|
+
var pattern = getPatternFromModuleFilePath(moduleFilePath, srcFolder);
|
|
3714
|
+
if (urlPath === "/") {
|
|
3715
|
+
var fullPath = "/";
|
|
3123
3716
|
root.pattern = getPatternFromModuleFilePath(moduleFilePath, srcFolder);
|
|
3124
3717
|
root.page = {
|
|
3125
3718
|
fullPath: fullPath
|
|
@@ -4082,13 +4675,21 @@ function checkGallerySchema(moduleSchema) {
|
|
|
4082
4675
|
};
|
|
4083
4676
|
}
|
|
4084
4677
|
function buildImageGalleryPatch(args, moduleSchema) {
|
|
4678
|
+
var _args$metadata$alt;
|
|
4085
4679
|
var wrong = checkGallerySchema(moduleSchema);
|
|
4086
4680
|
if (wrong) return wrong;
|
|
4087
4681
|
var remote = isRemoteSchema(moduleSchema);
|
|
4088
4682
|
var patch = [{
|
|
4089
4683
|
op: "add",
|
|
4090
4684
|
path: [args.filePath],
|
|
4091
|
-
value: _objectSpread2({}, args.metadata)
|
|
4685
|
+
value: _objectSpread2(_objectSpread2({}, args.metadata), {}, {
|
|
4686
|
+
// `alt` is a REQUIRED field of a gallery entry (`s.images()` gives it a
|
|
4687
|
+
// nullable string schema), so an entry written without one is invalid
|
|
4688
|
+
// the moment it lands — and the caller that had no alt to give is the
|
|
4689
|
+
// common case, not the odd one. Null is what the Studio's own upload
|
|
4690
|
+
// writes there.
|
|
4691
|
+
alt: (_args$metadata$alt = args.metadata.alt) !== null && _args$metadata$alt !== void 0 ? _args$metadata$alt : null
|
|
4692
|
+
})
|
|
4092
4693
|
}, {
|
|
4093
4694
|
op: "file",
|
|
4094
4695
|
path: [args.filePath],
|
|
@@ -4534,6 +5135,13 @@ function isSkippableFixCode(fix) {
|
|
|
4534
5135
|
// rewriting the module) and saving does not repair it either, so surface
|
|
4535
5136
|
// it: the message names the command (`val validate --fix`) that does.
|
|
4536
5137
|
return false;
|
|
5138
|
+
case "external:upload":
|
|
5139
|
+
// Same shape as `jsonValues:extract-entry` — an entry inlined in a
|
|
5140
|
+
// `.val.ts` that belongs elsewhere — and more emphatically not skippable.
|
|
5141
|
+
// Fixing it writes to a live store, which is why even the CLI does it only
|
|
5142
|
+
// under `val external upload` and never under a blanket `--fix`. Surfacing
|
|
5143
|
+
// it is the whole point: someone has to decide.
|
|
5144
|
+
return false;
|
|
4537
5145
|
default:
|
|
4538
5146
|
{
|
|
4539
5147
|
return false;
|
|
@@ -4573,15 +5181,723 @@ function filterBlockingValidationErrors(validationErrors, schemas, sources) {
|
|
|
4573
5181
|
return partitionValidationErrors(resolved).surfaced;
|
|
4574
5182
|
}
|
|
4575
5183
|
|
|
5184
|
+
/**
|
|
5185
|
+
* Every decision `encode` makes, with none of the pixels.
|
|
5186
|
+
*
|
|
5187
|
+
* The re-encode itself happens in two places that share nothing: the Studio
|
|
5188
|
+
* runs it on a `<canvas>` before the upload leaves the browser
|
|
5189
|
+
* (`packages/ui/spa/utils/encodeImage.ts`), and the MCP image tool runs it on
|
|
5190
|
+
* `sharp` in the server process (`@valbuild/mcp`). What must NOT differ between
|
|
5191
|
+
* them is what `s.image({ encode })` means — which images are converted, how
|
|
5192
|
+
* far they are scaled, and when the original wins anyway. So the decisions live
|
|
5193
|
+
* here, once, and each side supplies only its own encoder.
|
|
5194
|
+
*
|
|
5195
|
+
* See `architecture/media.md`.
|
|
5196
|
+
*/
|
|
5197
|
+
|
|
5198
|
+
var ENCODE_DEFAULTS = {
|
|
5199
|
+
quality: 0.8,
|
|
5200
|
+
maxWidth: 2560,
|
|
5201
|
+
maxHeight: 2560
|
|
5202
|
+
};
|
|
5203
|
+
var ENCODE_MIME_TYPE_OF = {
|
|
5204
|
+
webp: "image/webp"
|
|
5205
|
+
};
|
|
5206
|
+
|
|
5207
|
+
/**
|
|
5208
|
+
* Formats an encoder cannot round-trip without losing something: SVG is vector,
|
|
5209
|
+
* an animated GIF keeps only its first frame, and AVIF is already smaller than
|
|
5210
|
+
* anything we would produce.
|
|
5211
|
+
*/
|
|
5212
|
+
var NEVER_ENCODED = ["image/svg+xml", "image/gif", "image/avif"];
|
|
5213
|
+
|
|
5214
|
+
/** A dimension bound the encoder can act on, else the default. */
|
|
5215
|
+
function usableBound(value, fallback) {
|
|
5216
|
+
return value !== undefined && Number.isFinite(value) && value > 0 ? value : fallback;
|
|
5217
|
+
}
|
|
5218
|
+
|
|
5219
|
+
/**
|
|
5220
|
+
* The schema's own option first, then the gallery it references, then off.
|
|
5221
|
+
*
|
|
5222
|
+
* Mirrors how `ImageField` resolves `accept` and `directory`. A gallery-backed
|
|
5223
|
+
* field (`s.image(galleryVal)`) serializes with EMPTY options, so without the
|
|
5224
|
+
* gallery fallback it could never inherit what the gallery asked for — and
|
|
5225
|
+
* `s.image(galleryVal, { encode: false })` is how such a field opts back out.
|
|
5226
|
+
*
|
|
5227
|
+
* Nonsense numbers fall back to the defaults rather than through. `fitWithin`
|
|
5228
|
+
* reads a bound of 0 (or NaN, or a negative) as "everything already fits", so
|
|
5229
|
+
* an unusable `maxWidth` would otherwise SILENTLY disable the downscale it was
|
|
5230
|
+
* asking for, and a `quality` outside 0–1 is ignored by `canvas.toBlob` just as
|
|
5231
|
+
* quietly. Falling back is deliberately not throwing: a schema is evaluated by
|
|
5232
|
+
* the CLI's `node:vm` sandbox and by the Next server, so a typo'd quality would
|
|
5233
|
+
* take a whole project down over an upload-time optimisation.
|
|
5234
|
+
*/
|
|
5235
|
+
function resolveEncodeSettings(fieldOption, galleryOption) {
|
|
5236
|
+
var option = fieldOption !== undefined ? fieldOption : galleryOption;
|
|
5237
|
+
if (!option) {
|
|
5238
|
+
return null;
|
|
5239
|
+
}
|
|
5240
|
+
var quality = option.quality;
|
|
5241
|
+
return {
|
|
5242
|
+
type: option.type,
|
|
5243
|
+
quality: quality !== undefined && Number.isFinite(quality) && quality > 0 ? Math.min(quality, 1) : ENCODE_DEFAULTS.quality,
|
|
5244
|
+
maxWidth: usableBound(option.maxWidth, ENCODE_DEFAULTS.maxWidth),
|
|
5245
|
+
maxHeight: usableBound(option.maxHeight, ENCODE_DEFAULTS.maxHeight)
|
|
5246
|
+
};
|
|
5247
|
+
}
|
|
5248
|
+
|
|
5249
|
+
/**
|
|
5250
|
+
* Dimensions scaled to fit inside the box, or null when the image already does.
|
|
5251
|
+
*
|
|
5252
|
+
* Null is the signal for "no downscale was needed", which the size comparison
|
|
5253
|
+
* in {@link chooseEncoded} turns on.
|
|
5254
|
+
*/
|
|
5255
|
+
function fitWithin(width, height, maxWidth, maxHeight) {
|
|
5256
|
+
if (!(width > 0) || !(height > 0) || !(maxWidth > 0) || !(maxHeight > 0) || width <= maxWidth && height <= maxHeight) {
|
|
5257
|
+
return null;
|
|
5258
|
+
}
|
|
5259
|
+
var scale = Math.min(maxWidth / width, maxHeight / height);
|
|
5260
|
+
return {
|
|
5261
|
+
// At least one pixel: a very wide, very short image would otherwise round
|
|
5262
|
+
// its height to zero, and a zero-sized canvas cannot be encoded.
|
|
5263
|
+
width: Math.max(1, Math.round(width * scale)),
|
|
5264
|
+
height: Math.max(1, Math.round(height * scale))
|
|
5265
|
+
};
|
|
5266
|
+
}
|
|
5267
|
+
|
|
5268
|
+
/**
|
|
5269
|
+
* Whether a schema that declares `accept` would take this mime type.
|
|
5270
|
+
*
|
|
5271
|
+
* The same rules as `ImageSchema.executeValidate`, which validates the STORED
|
|
5272
|
+
* mimeType against `accept` — so converting to a type `accept` forbids would
|
|
5273
|
+
* upload a file the schema reports as invalid the moment it lands. Delegated to
|
|
5274
|
+
* `@valbuild/core` rather than re-spelled, because a second reading of one
|
|
5275
|
+
* `accept` string is how a converted image passes here and fails there.
|
|
5276
|
+
*/
|
|
5277
|
+
function isMimeTypeAccepted(mimeType, accept) {
|
|
5278
|
+
if (!accept) {
|
|
5279
|
+
return true;
|
|
5280
|
+
}
|
|
5281
|
+
return core.Internal.mimeTypeMatchesAccept(mimeType, accept);
|
|
5282
|
+
}
|
|
5283
|
+
|
|
5284
|
+
/** Formats left alone: the lossy list, plus the target type when it already fits. */
|
|
5285
|
+
function isSkippedSource(sourceMimeType, targetMimeType, needsDownscale) {
|
|
5286
|
+
if (NEVER_ENCODED.includes(sourceMimeType)) {
|
|
5287
|
+
return true;
|
|
5288
|
+
}
|
|
5289
|
+
return sourceMimeType === targetMimeType && !needsDownscale;
|
|
5290
|
+
}
|
|
5291
|
+
|
|
5292
|
+
/**
|
|
5293
|
+
* Whether to upload what the encoder produced, or the file that was picked.
|
|
5294
|
+
*
|
|
5295
|
+
* Two ways the encoded bytes lose. They are not actually the type we asked for
|
|
5296
|
+
* — `canvas.toBlob` answers an unsupported type with a PNG rather than with
|
|
5297
|
+
* null, so "not null" is not the same question as "is a webp". Or they are
|
|
5298
|
+
* simply bigger, which happens for small and flat images: measured, an 8x8
|
|
5299
|
+
* solid PNG of 74 bytes becomes a 548 byte webp. A downscale overrides that,
|
|
5300
|
+
* since the original is then the wrong size whatever it weighs.
|
|
5301
|
+
*/
|
|
5302
|
+
function chooseEncoded(input) {
|
|
5303
|
+
if (input.encodedSize === null || input.encodedType !== input.targetMimeType) {
|
|
5304
|
+
return false;
|
|
5305
|
+
}
|
|
5306
|
+
if (input.needsDownscale) {
|
|
5307
|
+
return true;
|
|
5308
|
+
}
|
|
5309
|
+
return input.encodedSize < input.originalSize;
|
|
5310
|
+
}
|
|
5311
|
+
|
|
5312
|
+
/**
|
|
5313
|
+
* `photo.png` -> `photo.webp`. The extension is re-derived downstream too, but
|
|
5314
|
+
* the name should not lie.
|
|
5315
|
+
*/
|
|
5316
|
+
function withExtension(filename, ext) {
|
|
5317
|
+
var base = filename.split(".").slice(0, -1).join(".") || filename;
|
|
5318
|
+
return "".concat(base, ".").concat(ext);
|
|
5319
|
+
}
|
|
5320
|
+
|
|
5321
|
+
/**
|
|
5322
|
+
* The one way to build a child's source path.
|
|
5323
|
+
*
|
|
5324
|
+
* There were five hand-rolled versions of this, three of them subtly different
|
|
5325
|
+
* and two of them wrong, and the wrong ones were wrong in a way that hid: a
|
|
5326
|
+
* module path segment has to be JSON-quoted if it is a string and bare if it is
|
|
5327
|
+
* an array index, and both broken copies appended the raw key at the module
|
|
5328
|
+
* root. An unquoted key parses fine as long as it contains no `.` — so every
|
|
5329
|
+
* object field and every ordinary record key still resolved, and only a key with
|
|
5330
|
+
* a dot in it broke. That is exactly what a media gallery is keyed by, so
|
|
5331
|
+
* clicking a file in the Media panel navigated to
|
|
5332
|
+
* `?p=/public/val/red-8x8_bfbd0.png`, which `splitModulePath` reads as the two
|
|
5333
|
+
* segments `/public/val/red-8x8_bfbd0` and `png`, and the studio said the module
|
|
5334
|
+
* was not found.
|
|
5335
|
+
*
|
|
5336
|
+
* So: one function, and never a template string. `Internal.createValPathOfItem`
|
|
5337
|
+
* is the rule itself — `JSON.stringify` quotes a string and leaves a number
|
|
5338
|
+
* bare, which is precisely the module-path grammar — and everything here defers
|
|
5339
|
+
* to it rather than restating it.
|
|
5340
|
+
*/
|
|
5341
|
+
function sourcePathOfChild(parent, key) {
|
|
5342
|
+
/**
|
|
5343
|
+
* A parent that is already `…?p=` gets the key with no separator.
|
|
5344
|
+
*
|
|
5345
|
+
* The search index walks from `moduleFilePath + "?p="` rather than from the
|
|
5346
|
+
* module file path, so that every path it produces has a module path — and
|
|
5347
|
+
* `createValPathOfItem` would see the separator and join with a `.`, giving
|
|
5348
|
+
* `?p=."title"`.
|
|
5349
|
+
*/
|
|
5350
|
+
if (parent.endsWith(core.Internal.ModuleFilePathSep)) {
|
|
5351
|
+
return "".concat(parent).concat(JSON.stringify(key));
|
|
5352
|
+
}
|
|
5353
|
+
var path = core.Internal.createValPathOfItem(parent, key);
|
|
5354
|
+
if (path === undefined) {
|
|
5355
|
+
// Only when the parent is empty, which callers cannot produce: every walk
|
|
5356
|
+
// starts from a module file path. Thrown rather than skipped so a future
|
|
5357
|
+
// change that breaks that assumption is loud.
|
|
5358
|
+
throw new Error("Could not build a source path for '".concat(key, "' under ").concat(parent));
|
|
5359
|
+
}
|
|
5360
|
+
return path;
|
|
5361
|
+
}
|
|
5362
|
+
|
|
5363
|
+
/**
|
|
5364
|
+
* The same thing, for callers holding the module file path and module path apart.
|
|
5365
|
+
*
|
|
5366
|
+
* `ModulePath` is empty at the module root, which is the case both broken copies
|
|
5367
|
+
* got wrong: there is no separator in the path yet, so the key is the first
|
|
5368
|
+
* segment and still has to be quoted.
|
|
5369
|
+
*/
|
|
5370
|
+
function concatModulePath(moduleFilePath, modulePath, key) {
|
|
5371
|
+
return sourcePathOfChild(modulePath ? core.Internal.joinModuleFilePathAndModulePath(moduleFilePath, modulePath) : moduleFilePath, key);
|
|
5372
|
+
}
|
|
5373
|
+
|
|
5374
|
+
/**
|
|
5375
|
+
* Traverses a schema and source pair, calling a callback for each leaf node.
|
|
5376
|
+
* Handles all schema types including primitives, objects, arrays, records, unions, richtext, files, images, etc.
|
|
5377
|
+
*
|
|
5378
|
+
* @param source - The JSON source data
|
|
5379
|
+
* @param schema - The serialized schema
|
|
5380
|
+
* @param path - The current source path
|
|
5381
|
+
* @param callback - Function called for each traversable node with { source, schema, path }
|
|
5382
|
+
*/
|
|
5383
|
+
function traverseSchemaSource(source, schema, path, callback) {
|
|
5384
|
+
if (!(schema !== null && schema !== void 0 && schema.type)) {
|
|
5385
|
+
throw new Error("Schema not found for " + path);
|
|
5386
|
+
}
|
|
5387
|
+
if (source === null) {
|
|
5388
|
+
return;
|
|
5389
|
+
}
|
|
5390
|
+
|
|
5391
|
+
// An un-loaded `.jsonValues()` entry is an opaque `{_type:"json"}` marker, not
|
|
5392
|
+
// the value its schema describes. Skip it — its content is traversed once
|
|
5393
|
+
// loaded, since the sync engine substitutes it in place of the marker.
|
|
5394
|
+
//
|
|
5395
|
+
// Without this the marker falls through to whichever branch its ITEM schema
|
|
5396
|
+
// selects: an object/record item walks the marker's OWN keys, so an entry
|
|
5397
|
+
// schema with a field named `_type` or `patch_id` would index the marker's
|
|
5398
|
+
// internals as if they were content.
|
|
5399
|
+
if (core.Internal.isJson(source)) {
|
|
5400
|
+
return;
|
|
5401
|
+
}
|
|
5402
|
+
|
|
5403
|
+
// Handle primitives
|
|
5404
|
+
if (schema.type === "string") {
|
|
5405
|
+
if (typeof source === "string") {
|
|
5406
|
+
callback({
|
|
5407
|
+
source: source,
|
|
5408
|
+
schema: schema,
|
|
5409
|
+
path: path
|
|
5410
|
+
});
|
|
5411
|
+
}
|
|
5412
|
+
return;
|
|
5413
|
+
}
|
|
5414
|
+
if (schema.type === "number") {
|
|
5415
|
+
if (typeof source === "number") {
|
|
5416
|
+
callback({
|
|
5417
|
+
source: source,
|
|
5418
|
+
schema: schema,
|
|
5419
|
+
path: path
|
|
5420
|
+
});
|
|
5421
|
+
}
|
|
5422
|
+
return;
|
|
5423
|
+
}
|
|
5424
|
+
if (schema.type === "boolean") {
|
|
5425
|
+
if (typeof source === "boolean") {
|
|
5426
|
+
callback({
|
|
5427
|
+
source: source,
|
|
5428
|
+
schema: schema,
|
|
5429
|
+
path: path
|
|
5430
|
+
});
|
|
5431
|
+
}
|
|
5432
|
+
return;
|
|
5433
|
+
}
|
|
5434
|
+
|
|
5435
|
+
// Handle richtext - flatten and call callback
|
|
5436
|
+
if (schema.type === "richtext") {
|
|
5437
|
+
callback({
|
|
5438
|
+
source: source,
|
|
5439
|
+
schema: schema,
|
|
5440
|
+
path: path
|
|
5441
|
+
});
|
|
5442
|
+
return;
|
|
5443
|
+
}
|
|
5444
|
+
|
|
5445
|
+
// Handle file/image
|
|
5446
|
+
if (schema.type === "file" || schema.type === "image") {
|
|
5447
|
+
if (source && _typeof(source) === "object" && "path" in source && typeof source.path === "string") {
|
|
5448
|
+
callback({
|
|
5449
|
+
source: source,
|
|
5450
|
+
schema: schema,
|
|
5451
|
+
path: path
|
|
5452
|
+
});
|
|
5453
|
+
}
|
|
5454
|
+
return;
|
|
5455
|
+
}
|
|
5456
|
+
|
|
5457
|
+
// Handle date
|
|
5458
|
+
if (schema.type === "date") {
|
|
5459
|
+
if (typeof source === "string") {
|
|
5460
|
+
callback({
|
|
5461
|
+
source: source,
|
|
5462
|
+
schema: schema,
|
|
5463
|
+
path: path
|
|
5464
|
+
});
|
|
5465
|
+
}
|
|
5466
|
+
return;
|
|
5467
|
+
}
|
|
5468
|
+
|
|
5469
|
+
// Handle dateTime
|
|
5470
|
+
if (schema.type === "dateTime") {
|
|
5471
|
+
if (typeof source === "string") {
|
|
5472
|
+
callback({
|
|
5473
|
+
source: source,
|
|
5474
|
+
schema: schema,
|
|
5475
|
+
path: path
|
|
5476
|
+
});
|
|
5477
|
+
}
|
|
5478
|
+
return;
|
|
5479
|
+
}
|
|
5480
|
+
|
|
5481
|
+
// Handle color
|
|
5482
|
+
if (schema.type === "color") {
|
|
5483
|
+
if (typeof source === "string") {
|
|
5484
|
+
callback({
|
|
5485
|
+
source: source,
|
|
5486
|
+
schema: schema,
|
|
5487
|
+
path: path
|
|
5488
|
+
});
|
|
5489
|
+
}
|
|
5490
|
+
return;
|
|
5491
|
+
}
|
|
5492
|
+
|
|
5493
|
+
// Handle code
|
|
5494
|
+
if (schema.type === "code") {
|
|
5495
|
+
if (typeof source === "string") {
|
|
5496
|
+
callback({
|
|
5497
|
+
source: source,
|
|
5498
|
+
schema: schema,
|
|
5499
|
+
path: path
|
|
5500
|
+
});
|
|
5501
|
+
}
|
|
5502
|
+
return;
|
|
5503
|
+
}
|
|
5504
|
+
|
|
5505
|
+
// Handle keyOf
|
|
5506
|
+
if (schema.type === "keyOf") {
|
|
5507
|
+
if (typeof source === "string" || typeof source === "number") {
|
|
5508
|
+
callback({
|
|
5509
|
+
source: source,
|
|
5510
|
+
schema: schema,
|
|
5511
|
+
path: path
|
|
5512
|
+
});
|
|
5513
|
+
}
|
|
5514
|
+
return;
|
|
5515
|
+
}
|
|
5516
|
+
|
|
5517
|
+
// Handle literal
|
|
5518
|
+
if (schema.type === "literal") {
|
|
5519
|
+
callback({
|
|
5520
|
+
source: source,
|
|
5521
|
+
schema: schema,
|
|
5522
|
+
path: path
|
|
5523
|
+
});
|
|
5524
|
+
return;
|
|
5525
|
+
}
|
|
5526
|
+
|
|
5527
|
+
// Handle route
|
|
5528
|
+
if (schema.type === "route") {
|
|
5529
|
+
if (typeof source === "string") {
|
|
5530
|
+
callback({
|
|
5531
|
+
source: source,
|
|
5532
|
+
schema: schema,
|
|
5533
|
+
path: path
|
|
5534
|
+
});
|
|
5535
|
+
}
|
|
5536
|
+
return;
|
|
5537
|
+
}
|
|
5538
|
+
|
|
5539
|
+
// Handle array
|
|
5540
|
+
if (schema.type === "array") {
|
|
5541
|
+
if (!Array.isArray(source)) {
|
|
5542
|
+
return;
|
|
5543
|
+
}
|
|
5544
|
+
if (!schema.item) {
|
|
5545
|
+
throw new Error("Schema (" + schema.type + ") item not found for " + path);
|
|
5546
|
+
}
|
|
5547
|
+
for (var i = 0; i < source.length; i++) {
|
|
5548
|
+
var subPath = sourcePathOfChild(path, i);
|
|
5549
|
+
traverseSchemaSource(source[i], schema.item, subPath, callback);
|
|
5550
|
+
}
|
|
5551
|
+
return;
|
|
5552
|
+
}
|
|
5553
|
+
|
|
5554
|
+
// Handle object, record and settings
|
|
5555
|
+
if (schema.type === "object" || schema.type === "record" ||
|
|
5556
|
+
// Settings walks like an object, and starting from the SOURCE is what makes
|
|
5557
|
+
// its optional keys work: an absent section has nothing to traverse.
|
|
5558
|
+
schema.type === "settings") {
|
|
5559
|
+
if (_typeof(source) !== "object" || source === null || Array.isArray(source)) {
|
|
5560
|
+
return;
|
|
5561
|
+
}
|
|
5562
|
+
for (var key in source) {
|
|
5563
|
+
var _schema$items;
|
|
5564
|
+
var subSchema = schema.type === "record" ? schema.item : (_schema$items = schema.items) === null || _schema$items === void 0 ? void 0 : _schema$items[key];
|
|
5565
|
+
if (!subSchema) {
|
|
5566
|
+
continue;
|
|
5567
|
+
}
|
|
5568
|
+
var _subPath = sourcePathOfChild(path, key);
|
|
5569
|
+
traverseSchemaSource(source[key], subSchema, _subPath, callback);
|
|
5570
|
+
}
|
|
5571
|
+
return;
|
|
5572
|
+
}
|
|
5573
|
+
|
|
5574
|
+
// Handle union
|
|
5575
|
+
if (schema.type === "union") {
|
|
5576
|
+
var schemaKey = schema.key;
|
|
5577
|
+
if (typeof schemaKey === "string") {
|
|
5578
|
+
// Tagged union - find matching sub-schema
|
|
5579
|
+
if (source && _typeof(source) === "object" && !Array.isArray(source) && schemaKey in source) {
|
|
5580
|
+
var itemKey = source[schemaKey];
|
|
5581
|
+
if (typeof itemKey === "string") {
|
|
5582
|
+
var schemaOfItem = schema.items.filter(function (item) {
|
|
5583
|
+
return item.type === "object";
|
|
5584
|
+
}).find(function (item) {
|
|
5585
|
+
var itemKeySchema = item.items[schemaKey];
|
|
5586
|
+
if ((itemKeySchema === null || itemKeySchema === void 0 ? void 0 : itemKeySchema.type) === "literal") {
|
|
5587
|
+
return itemKeySchema.value === itemKey;
|
|
5588
|
+
}
|
|
5589
|
+
return false;
|
|
5590
|
+
});
|
|
5591
|
+
if (schemaOfItem) {
|
|
5592
|
+
traverseSchemaSource(source, schemaOfItem, path, callback);
|
|
5593
|
+
}
|
|
5594
|
+
}
|
|
5595
|
+
}
|
|
5596
|
+
} else {
|
|
5597
|
+
// Literal union - treat as primitive
|
|
5598
|
+
if (typeof source === "string") {
|
|
5599
|
+
callback({
|
|
5600
|
+
source: source,
|
|
5601
|
+
schema: schema,
|
|
5602
|
+
path: path
|
|
5603
|
+
});
|
|
5604
|
+
}
|
|
5605
|
+
}
|
|
5606
|
+
return;
|
|
5607
|
+
}
|
|
5608
|
+
|
|
5609
|
+
// Exhaustive check
|
|
5610
|
+
var exhaustiveCheck = schema;
|
|
5611
|
+
throw new Error("Unsupported schema type: " + JSON.stringify(exhaustiveCheck));
|
|
5612
|
+
}
|
|
5613
|
+
|
|
5614
|
+
/**
|
|
5615
|
+
* Flattens richtext source to a plain string, extracting only text content.
|
|
5616
|
+
* Used for indexing richtext content in search.
|
|
5617
|
+
*/
|
|
5618
|
+
function flattenRichText(source) {
|
|
5619
|
+
var res = "";
|
|
5620
|
+
function rec(child) {
|
|
5621
|
+
if (typeof child === "string") {
|
|
5622
|
+
res += child;
|
|
5623
|
+
} else {
|
|
5624
|
+
if (child && _typeof(child) === "object" && "children" in child && Array.isArray(child.children)) {
|
|
5625
|
+
var _iterator = _createForOfIteratorHelper(child.children),
|
|
5626
|
+
_step;
|
|
5627
|
+
try {
|
|
5628
|
+
for (_iterator.s(); !(_step = _iterator.n()).done;) {
|
|
5629
|
+
var c = _step.value;
|
|
5630
|
+
rec(c);
|
|
5631
|
+
}
|
|
5632
|
+
} catch (err) {
|
|
5633
|
+
_iterator.e(err);
|
|
5634
|
+
} finally {
|
|
5635
|
+
_iterator.f();
|
|
5636
|
+
}
|
|
5637
|
+
}
|
|
5638
|
+
}
|
|
5639
|
+
}
|
|
5640
|
+
rec({
|
|
5641
|
+
children: source
|
|
5642
|
+
});
|
|
5643
|
+
return res;
|
|
5644
|
+
}
|
|
5645
|
+
|
|
5646
|
+
/**
|
|
5647
|
+
* Resolves a file ref (local path or remote URL) to a clean local-style path.
|
|
5648
|
+
*/
|
|
5649
|
+
function cleanRefPath(ref) {
|
|
5650
|
+
var remoteRefRes = core.Internal.remote.splitRemoteRef(ref);
|
|
5651
|
+
return remoteRefRes.status === "success" ? "/".concat(remoteRefRes.filePath) : ref;
|
|
5652
|
+
}
|
|
5653
|
+
|
|
5654
|
+
/**
|
|
5655
|
+
* Extract a human-readable filename from a file ref (local path or remote URL).
|
|
5656
|
+
* Handles both remote refs (via splitRemoteRef) and plain `/public/...` paths.
|
|
5657
|
+
*/
|
|
5658
|
+
function getFilenameFromRef(ref) {
|
|
5659
|
+
var cleanPath = cleanRefPath(ref);
|
|
5660
|
+
return cleanPath.split("/").pop() || cleanPath;
|
|
5661
|
+
}
|
|
5662
|
+
|
|
5663
|
+
/**
|
|
5664
|
+
* Parse a file ref into its constituent parts: a clean path, filename, and
|
|
5665
|
+
* folder (the path relative to `/public`).
|
|
5666
|
+
*
|
|
5667
|
+
* The `folder` strips the standard `/public` prefix so it shows a
|
|
5668
|
+
* concise location like `/images` instead of `/public/images`.
|
|
5669
|
+
*/
|
|
5670
|
+
function getRefParts(ref) {
|
|
5671
|
+
var cleanPath = cleanRefPath(ref);
|
|
5672
|
+
var filename = cleanPath.split("/").pop() || cleanPath;
|
|
5673
|
+
// Only strip a leading "/public" path segment: "/publicity/a.png" must keep its folder
|
|
5674
|
+
var folder = cleanPath.replace(/^\/public(?=\/|$)/, "").replace(/\/[^/]+$/, "") || "/";
|
|
5675
|
+
return {
|
|
5676
|
+
cleanPath: cleanPath,
|
|
5677
|
+
filename: filename,
|
|
5678
|
+
folder: folder
|
|
5679
|
+
};
|
|
5680
|
+
}
|
|
5681
|
+
|
|
5682
|
+
/**
|
|
5683
|
+
* The search index and the labels to render for its hits.
|
|
5684
|
+
*
|
|
5685
|
+
* Kept as a plain module rather than living inside a worker so it can be tested:
|
|
5686
|
+
* a worker entry runs `self.onmessage` on import, which no test environment here
|
|
5687
|
+
* provides.
|
|
5688
|
+
*
|
|
5689
|
+
* In `@valbuild/shared` rather than the Studio because there are now two
|
|
5690
|
+
* realms searching the same content: the Studio's `SearchStore`, which keeps an
|
|
5691
|
+
* index alive in a worker and re-indexes a module at a time, and the MCP
|
|
5692
|
+
* `search_content` tool, which builds one per call and throws it away. What
|
|
5693
|
+
* counts as a document, and what a hit is labelled, must not differ between
|
|
5694
|
+
* them — an agent and an editor searching the same project should find the same
|
|
5695
|
+
* things.
|
|
5696
|
+
*/
|
|
5697
|
+
|
|
5698
|
+
/** An index with nothing in it. Fill it with {@link indexModule}. */
|
|
5699
|
+
function createSearchIndex() {
|
|
5700
|
+
return {
|
|
5701
|
+
index: new FlexSearch__default["default"].Index({
|
|
5702
|
+
tokenize: "forward"
|
|
5703
|
+
}),
|
|
5704
|
+
pathToLabel: new Map(),
|
|
5705
|
+
docsByModule: new Map()
|
|
5706
|
+
};
|
|
5707
|
+
}
|
|
5708
|
+
|
|
5709
|
+
/**
|
|
5710
|
+
* Index one module, replacing whatever was previously indexed for it.
|
|
5711
|
+
*
|
|
5712
|
+
* Remove-then-add rather than `update`, because the set of paths in a module is
|
|
5713
|
+
* not stable: deleting an array item or a record entry means documents that must
|
|
5714
|
+
* disappear, and `update` only revises ids it is given.
|
|
5715
|
+
*
|
|
5716
|
+
* `.jsonValues()` entries are indexed only once their content is loaded — an
|
|
5717
|
+
* un-loaded entry is an opaque marker that {@link traverseSchemaSource} skips, so
|
|
5718
|
+
* the index is naturally PARTIAL and grows as batches land. That is why the search
|
|
5719
|
+
* UI loads the entries on the first query and re-indexes (debounced) as they
|
|
5720
|
+
* arrive, rather than pretending an empty result set is an answer.
|
|
5721
|
+
*/
|
|
5722
|
+
function indexModule(searchIndex, moduleFilePath, source, schema) {
|
|
5723
|
+
var index = searchIndex.index,
|
|
5724
|
+
pathToLabel = searchIndex.pathToLabel,
|
|
5725
|
+
docsByModule = searchIndex.docsByModule;
|
|
5726
|
+
removeModule(searchIndex, moduleFilePath);
|
|
5727
|
+
var docs = new Set();
|
|
5728
|
+
docsByModule.set(moduleFilePath, docs);
|
|
5729
|
+
var path = moduleFilePath + "?p=";
|
|
5730
|
+
traverseSchemaSource(source, schema, path, function (_ref) {
|
|
5731
|
+
var source = _ref.source,
|
|
5732
|
+
schema = _ref.schema,
|
|
5733
|
+
path = _ref.path;
|
|
5734
|
+
if (source === null) {
|
|
5735
|
+
return;
|
|
5736
|
+
}
|
|
5737
|
+
var searchText = "";
|
|
5738
|
+
var label = "";
|
|
5739
|
+
|
|
5740
|
+
// Handle primitives
|
|
5741
|
+
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") {
|
|
5742
|
+
var _source$toString, _source$toString2;
|
|
5743
|
+
searchText = (_source$toString = source === null || source === void 0 ? void 0 : source.toString()) !== null && _source$toString !== void 0 ? _source$toString : "";
|
|
5744
|
+
label = (_source$toString2 = source === null || source === void 0 ? void 0 : source.toString()) !== null && _source$toString2 !== void 0 ? _source$toString2 : "";
|
|
5745
|
+
} else if (schema.type === "literal") {
|
|
5746
|
+
searchText = schema.value.toString();
|
|
5747
|
+
label = schema.value.toString();
|
|
5748
|
+
}
|
|
5749
|
+
// Handle richtext - flatten to get text content
|
|
5750
|
+
else if (schema.type === "richtext") {
|
|
5751
|
+
searchText = flattenRichText(source);
|
|
5752
|
+
// Use first 50 chars as label
|
|
5753
|
+
label = searchText.substring(0, 50) || "richtext";
|
|
5754
|
+
}
|
|
5755
|
+
// Handle file/image
|
|
5756
|
+
else if (schema.type === "file" || schema.type === "image") {
|
|
5757
|
+
if (source !== null && _typeof(source) === "object" && "path" in source && typeof source.path === "string") {
|
|
5758
|
+
// The label is the bare filename - the folder is shown separately in
|
|
5759
|
+
// the UI - but both are searchable.
|
|
5760
|
+
var _getRefParts = getRefParts(source.path),
|
|
5761
|
+
filename = _getRefParts.filename,
|
|
5762
|
+
folder = _getRefParts.folder;
|
|
5763
|
+
var alt = "alt" in source ? source.alt : "";
|
|
5764
|
+
searchText = filename + " " + folder + " " + alt;
|
|
5765
|
+
label = filename;
|
|
5766
|
+
}
|
|
5767
|
+
}
|
|
5768
|
+
|
|
5769
|
+
// Add to index if we have search text
|
|
5770
|
+
if (searchText) {
|
|
5771
|
+
var _Internal$splitModule = core.Internal.splitModuleFilePathAndModulePath(path),
|
|
5772
|
+
_Internal$splitModule2 = _slicedToArray(_Internal$splitModule, 2),
|
|
5773
|
+
modulePath = _Internal$splitModule2[1];
|
|
5774
|
+
var cleanPath = fastRemoveNonWordChars(modulePath) + " ";
|
|
5775
|
+
index.add(path, cleanPath + " " + path + " " + searchText);
|
|
5776
|
+
pathToLabel.set(path, label);
|
|
5777
|
+
docs.add(path);
|
|
5778
|
+
}
|
|
5779
|
+
});
|
|
5780
|
+
}
|
|
5781
|
+
|
|
5782
|
+
/**
|
|
5783
|
+
* Drop everything indexed for one module.
|
|
5784
|
+
*
|
|
5785
|
+
* Also used for a module that has gone away entirely — otherwise its documents
|
|
5786
|
+
* stay searchable and a hit navigates to a path that no longer exists.
|
|
5787
|
+
*/
|
|
5788
|
+
function removeModule(searchIndex, moduleFilePath) {
|
|
5789
|
+
var index = searchIndex.index,
|
|
5790
|
+
pathToLabel = searchIndex.pathToLabel,
|
|
5791
|
+
docsByModule = searchIndex.docsByModule;
|
|
5792
|
+
var existing = docsByModule.get(moduleFilePath);
|
|
5793
|
+
if (existing === undefined) return;
|
|
5794
|
+
var _iterator = _createForOfIteratorHelper(existing),
|
|
5795
|
+
_step;
|
|
5796
|
+
try {
|
|
5797
|
+
for (_iterator.s(); !(_step = _iterator.n()).done;) {
|
|
5798
|
+
var id = _step.value;
|
|
5799
|
+
index.remove(id);
|
|
5800
|
+
pathToLabel["delete"](id);
|
|
5801
|
+
}
|
|
5802
|
+
} catch (err) {
|
|
5803
|
+
_iterator.e(err);
|
|
5804
|
+
} finally {
|
|
5805
|
+
_iterator.f();
|
|
5806
|
+
}
|
|
5807
|
+
docsByModule["delete"](moduleFilePath);
|
|
5808
|
+
}
|
|
5809
|
+
|
|
5810
|
+
/**
|
|
5811
|
+
* Index every module, from scratch.
|
|
5812
|
+
*
|
|
5813
|
+
* Now a loop over {@link indexModule} rather than its own walk, so the full
|
|
5814
|
+
* build and the incremental one cannot drift in what they consider a document.
|
|
5815
|
+
*/
|
|
5816
|
+
function buildSearchIndex(modules) {
|
|
5817
|
+
var searchIndex = createSearchIndex();
|
|
5818
|
+
for (var moduleFilePathS in modules) {
|
|
5819
|
+
var moduleFilePath = moduleFilePathS;
|
|
5820
|
+
var _modules$moduleFilePa = modules[moduleFilePath],
|
|
5821
|
+
source = _modules$moduleFilePa.source,
|
|
5822
|
+
schema = _modules$moduleFilePa.schema;
|
|
5823
|
+
indexModule(searchIndex, moduleFilePath, source, schema);
|
|
5824
|
+
}
|
|
5825
|
+
return searchIndex;
|
|
5826
|
+
}
|
|
5827
|
+
var NON_CHARS = new Set(["/", "-", "_", ".", ":", "?", "&", "=", "@", '"', "'"]);
|
|
5828
|
+
function fastRemoveNonWordChars(str) {
|
|
5829
|
+
var result = "";
|
|
5830
|
+
for (var i = 0; i < str.length; i++) {
|
|
5831
|
+
var _char = str[i];
|
|
5832
|
+
if (!NON_CHARS.has(_char)) {
|
|
5833
|
+
result += _char;
|
|
5834
|
+
} else {
|
|
5835
|
+
result += " ";
|
|
5836
|
+
}
|
|
5837
|
+
}
|
|
5838
|
+
return result;
|
|
5839
|
+
}
|
|
5840
|
+
|
|
5841
|
+
/**
|
|
5842
|
+
* How many hits are counted before `total` stops being a count.
|
|
5843
|
+
*
|
|
5844
|
+
* FlexSearch stops as soon as it has the number of ids it was asked for, so a
|
|
5845
|
+
* `total` taken from a page-sized search is just the page size again — which
|
|
5846
|
+
* reads as "that is all there is" to anyone who did not write it. Counting is
|
|
5847
|
+
* asking for more ids than the page needs, so it costs an array of ids and no
|
|
5848
|
+
* more; this bound is where that stops being free, and `totalIsLowerBound` says
|
|
5849
|
+
* it was reached rather than letting the number quietly lie.
|
|
5850
|
+
*/
|
|
5851
|
+
var MAX_COUNTED_RESULTS = 10000;
|
|
5852
|
+
function performSearch(searchIndex, query) {
|
|
5853
|
+
var limit = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 50;
|
|
5854
|
+
var offset = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 0;
|
|
5855
|
+
if (searchIndex === null || !query.trim()) {
|
|
5856
|
+
return {
|
|
5857
|
+
results: [],
|
|
5858
|
+
total: 0,
|
|
5859
|
+
totalIsLowerBound: false
|
|
5860
|
+
};
|
|
5861
|
+
}
|
|
5862
|
+
var index = searchIndex.index,
|
|
5863
|
+
pathToLabel = searchIndex.pathToLabel;
|
|
5864
|
+
var counted = Math.max(offset + limit, MAX_COUNTED_RESULTS);
|
|
5865
|
+
var searchResults = index.search(query, {
|
|
5866
|
+
limit: counted
|
|
5867
|
+
});
|
|
5868
|
+
var total = searchResults.length;
|
|
5869
|
+
var paged = searchResults.slice(offset, offset + limit);
|
|
5870
|
+
return {
|
|
5871
|
+
results: paged.map(function (id) {
|
|
5872
|
+
return {
|
|
5873
|
+
path: id,
|
|
5874
|
+
label: pathToLabel.get(id) || id
|
|
5875
|
+
};
|
|
5876
|
+
}),
|
|
5877
|
+
total: total,
|
|
5878
|
+
totalIsLowerBound: total >= counted
|
|
5879
|
+
};
|
|
5880
|
+
}
|
|
5881
|
+
|
|
4576
5882
|
exports.Api = Api;
|
|
5883
|
+
exports.BinaryFileRef = BinaryFileRef;
|
|
4577
5884
|
exports.CodeOptions = CodeOptions;
|
|
4578
5885
|
exports.ColorOptions = ColorOptions;
|
|
4579
5886
|
exports.DEFAULT_ERROR_MESSAGE = DEFAULT_ERROR_MESSAGE;
|
|
4580
5887
|
exports.DateOptions = DateOptions;
|
|
5888
|
+
exports.ENCODE_DEFAULTS = ENCODE_DEFAULTS;
|
|
5889
|
+
exports.ENCODE_MIME_TYPE_OF = ENCODE_MIME_TYPE_OF;
|
|
4581
5890
|
exports.FileOptions = FileOptions;
|
|
5891
|
+
exports.HistoricalCommit = HistoricalCommit;
|
|
5892
|
+
exports.HistoricalModule = HistoricalModule;
|
|
5893
|
+
exports.HistoricalPatchSet = HistoricalPatchSet;
|
|
5894
|
+
exports.HistoryError = HistoryError;
|
|
4582
5895
|
exports.ImageEncodeOption = ImageEncodeOption;
|
|
4583
5896
|
exports.ImageOptions = ImageOptions;
|
|
5897
|
+
exports.JSONValue = JSONValue;
|
|
4584
5898
|
exports.JSON_ENTRIES_BATCH_MAX = JSON_ENTRIES_BATCH_MAX;
|
|
5899
|
+
exports.ModuleComparison = ModuleComparison;
|
|
5900
|
+
exports.NEVER_ENCODED = NEVER_ENCODED;
|
|
4585
5901
|
exports.ParentRef = ParentRef$1;
|
|
4586
5902
|
exports.Patch = Patch;
|
|
4587
5903
|
exports.PatchBlock = PatchBlock;
|
|
@@ -4599,6 +5915,7 @@ exports.RemirrorOrderedList = RemirrorOrderedList;
|
|
|
4599
5915
|
exports.RemirrorParagraph = RemirrorParagraph;
|
|
4600
5916
|
exports.RemirrorText = RemirrorText;
|
|
4601
5917
|
exports.RemirrorTextMark = RemirrorTextMark;
|
|
5918
|
+
exports.RestoreVerdict = RestoreVerdict;
|
|
4602
5919
|
exports.RichTextOptions = RichTextOptions;
|
|
4603
5920
|
exports.SerializedArraySchema = SerializedArraySchema;
|
|
4604
5921
|
exports.SerializedBooleanSchema = SerializedBooleanSchema;
|
|
@@ -4616,6 +5933,7 @@ exports.SerializedRecordSchema = SerializedRecordSchema;
|
|
|
4616
5933
|
exports.SerializedRichTextSchema = SerializedRichTextSchema;
|
|
4617
5934
|
exports.SerializedRouteSchema = SerializedRouteSchema;
|
|
4618
5935
|
exports.SerializedSchema = SerializedSchema;
|
|
5936
|
+
exports.SerializedSettingsSchema = SerializedSettingsSchema;
|
|
4619
5937
|
exports.SerializedStringSchema = SerializedStringSchema;
|
|
4620
5938
|
exports.SerializedUnionSchema = SerializedUnionSchema;
|
|
4621
5939
|
exports.SharedValConfig = SharedValConfig;
|
|
@@ -4634,22 +5952,35 @@ exports.buildEmptyAtPathPatch = buildEmptyAtPathPatch;
|
|
|
4634
5952
|
exports.buildFileRefValue = buildFileRefValue;
|
|
4635
5953
|
exports.buildImageGalleryPatch = buildImageGalleryPatch;
|
|
4636
5954
|
exports.buildRemoveImageGalleryEntryPatch = buildRemoveImageGalleryEntryPatch;
|
|
5955
|
+
exports.buildSearchIndex = buildSearchIndex;
|
|
5956
|
+
exports.checkCompatibility = checkCompatibility;
|
|
5957
|
+
exports.chooseEncoded = chooseEncoded;
|
|
5958
|
+
exports.concatModulePath = concatModulePath;
|
|
4637
5959
|
exports.createRegExpFromPattern = createRegExpFromPattern;
|
|
5960
|
+
exports.createSearchIndex = createSearchIndex;
|
|
4638
5961
|
exports.createValClient = createValClient;
|
|
4639
5962
|
exports.describeContainerAtPath = describeContainerAtPath;
|
|
4640
5963
|
exports.emptyOf = emptyOf;
|
|
5964
|
+
exports.explainIncompatible = explainIncompatible;
|
|
4641
5965
|
exports.filterBlockingValidationErrors = filterBlockingValidationErrors;
|
|
4642
5966
|
exports.filterRoutesByPatterns = filterRoutesByPatterns;
|
|
4643
5967
|
exports.findSimilar = findSimilar;
|
|
5968
|
+
exports.fitWithin = fitWithin;
|
|
5969
|
+
exports.flattenRichText = flattenRichText;
|
|
4644
5970
|
exports.getErrorMessageFromUnknownJson = getErrorMessageFromUnknownJson;
|
|
5971
|
+
exports.getFilenameFromRef = getFilenameFromRef;
|
|
4645
5972
|
exports.getInlineImgInfo = getInlineImgInfo;
|
|
4646
5973
|
exports.getNextAppRouterSitemapTree = getNextAppRouterSitemapTree;
|
|
4647
5974
|
exports.getNextAppRouterSourceFolder = getNextAppRouterSourceFolder;
|
|
4648
5975
|
exports.getPatternFromModuleFilePath = getPatternFromModuleFilePath;
|
|
5976
|
+
exports.getRefParts = getRefParts;
|
|
4649
5977
|
exports.getRoutesOf = getRoutesOf;
|
|
4650
5978
|
exports.getRoutesWithModulePaths = getRoutesWithModulePaths;
|
|
4651
5979
|
exports.getSourceAt = getSourceAt;
|
|
5980
|
+
exports.indexModule = indexModule;
|
|
5981
|
+
exports.isMimeTypeAccepted = isMimeTypeAccepted;
|
|
4652
5982
|
exports.isRemoteSchema = isRemoteSchema;
|
|
5983
|
+
exports.isSkippedSource = isSkippedSource;
|
|
4653
5984
|
exports.isValCanvasFrame = isValCanvasFrame;
|
|
4654
5985
|
exports.isValCanvasPageMessage = isValCanvasPageMessage;
|
|
4655
5986
|
exports.isValCanvasStudioMessage = isValCanvasStudioMessage;
|
|
@@ -4657,13 +5988,19 @@ exports.levenshtein = levenshtein;
|
|
|
4657
5988
|
exports.newestCommitSha = newestCommitSha;
|
|
4658
5989
|
exports.parseRoutePattern = parseRoutePattern;
|
|
4659
5990
|
exports.partitionValidationErrors = partitionValidationErrors;
|
|
5991
|
+
exports.performSearch = performSearch;
|
|
4660
5992
|
exports.remirrorToRichTextSource = remirrorToRichTextSource;
|
|
5993
|
+
exports.removeModule = removeModule;
|
|
5994
|
+
exports.resolveEncodeSettings = resolveEncodeSettings;
|
|
4661
5995
|
exports.resolveSchemaSourceFixForError = resolveSchemaSourceFixForError;
|
|
4662
5996
|
exports.resolveSchemaSourceFixes = resolveSchemaSourceFixes;
|
|
4663
5997
|
exports.resolveSerializedSchemaAtPath = resolveSerializedSchemaAtPath;
|
|
4664
5998
|
exports.richTextToRemirror = richTextToRemirror;
|
|
4665
5999
|
exports.safeParsePatch = safeParsePatch;
|
|
6000
|
+
exports.sourcePathOfChild = sourcePathOfChild;
|
|
4666
6001
|
exports.toolNames = toolNames;
|
|
6002
|
+
exports.traverseSchemaSource = traverseSchemaSource;
|
|
4667
6003
|
exports.urlOf = urlOf;
|
|
4668
6004
|
exports.validateRoutePatterns = validateRoutePatterns;
|
|
6005
|
+
exports.withExtension = withExtension;
|
|
4669
6006
|
exports.withValCanvasParam = withValCanvasParam;
|