@valbuild/shared 0.121.0 → 0.122.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 +76 -0
- package/dist/declarations/src/internal/ApiRoutes.d.ts +304 -41
- package/dist/declarations/src/internal/index.d.ts +2 -0
- package/dist/declarations/src/internal/schema/compatibility.d.ts +62 -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 +649 -43
- package/internal/dist/valbuild-shared-internal.cjs.prod.js +649 -43
- package/internal/dist/valbuild-shared-internal.esm.js +639 -44
- package/package.json +1 -1
|
@@ -897,6 +897,12 @@ function _asyncToGenerator(n) {
|
|
|
897
897
|
};
|
|
898
898
|
}
|
|
899
899
|
|
|
900
|
+
/**
|
|
901
|
+
* JSON, recursively — the shape of a Source over the wire.
|
|
902
|
+
*
|
|
903
|
+
* Exported because history reads stored Sources back and needs the same
|
|
904
|
+
* definition; a second copy is a second thing to keep in step.
|
|
905
|
+
*/
|
|
900
906
|
var JSONValue = z.lazy(function () {
|
|
901
907
|
return z.union([z.string(), z.number(), z["boolean"](), z["null"](), z.array(JSONValue), z.record(z.string(), JSONValue)]);
|
|
902
908
|
});
|
|
@@ -1275,7 +1281,26 @@ var SerializedRouteSchema = z.object({
|
|
|
1275
1281
|
readonly: z["boolean"]().optional(),
|
|
1276
1282
|
hidden: z["boolean"]().optional()
|
|
1277
1283
|
});
|
|
1278
|
-
|
|
1284
|
+
|
|
1285
|
+
// A settings module, and each section inside one, serialize as this - the
|
|
1286
|
+
// shape is recursive because `items` holds sections which are themselves
|
|
1287
|
+
// settings schemas. `s.settings()` never writes render/preview/customValidate
|
|
1288
|
+
// (see core/src/schema/settings.ts), but they are accepted here because they
|
|
1289
|
+
// are part of the declared type.
|
|
1290
|
+
var SerializedSettingsSchema = z.lazy(function () {
|
|
1291
|
+
return z.object({
|
|
1292
|
+
type: z.literal("settings"),
|
|
1293
|
+
render: FieldRender,
|
|
1294
|
+
preview: z.literal(true).optional(),
|
|
1295
|
+
items: z.record(z.string(), SerializedSchema),
|
|
1296
|
+
opt: z["boolean"](),
|
|
1297
|
+
customValidate: z["boolean"]().optional(),
|
|
1298
|
+
readonly: z["boolean"]().optional(),
|
|
1299
|
+
hidden: z["boolean"]().optional(),
|
|
1300
|
+
description: z.string().optional()
|
|
1301
|
+
});
|
|
1302
|
+
});
|
|
1303
|
+
var SerializedSchema = z.union([SerializedStringSchema, SerializedLiteralSchema, SerializedBooleanSchema, SerializedNumberSchema, SerializedObjectSchema, SerializedArraySchema, SerializedUnionSchema, SerializedRichTextSchema, SerializedRecordSchema, SerializedKeyOfSchema, SerializedRouteSchema, SerializedFileSchema, SerializedDateSchema, SerializedDateTimeSchema, SerializedColorSchema, SerializedCodeSchema, SerializedSettingsSchema, SerializedImageSchema]);
|
|
1279
1304
|
|
|
1280
1305
|
var ValCommit = z.object({
|
|
1281
1306
|
commitSha: z.string(),
|
|
@@ -1287,6 +1312,125 @@ var ValCommit = z.object({
|
|
|
1287
1312
|
commitMessage: z.string().nullable()
|
|
1288
1313
|
});
|
|
1289
1314
|
|
|
1315
|
+
/**
|
|
1316
|
+
* The wire shapes for reading history.
|
|
1317
|
+
*
|
|
1318
|
+
* Here rather than in `@valbuild/server` because both sides of `/api/val` need
|
|
1319
|
+
* them: the server builds these and the Studio parses them, and a second
|
|
1320
|
+
* hand-written copy in the client is how the two drift.
|
|
1321
|
+
*
|
|
1322
|
+
* `HistoryError` is a closed union of everything that can go wrong
|
|
1323
|
+
* reconstructing a commit - see `packages/server/src/history/HistoryError.ts`
|
|
1324
|
+
* for what each one means. It travels as data rather than as a message because
|
|
1325
|
+
* the Studio decides what to OFFER from it: a schema mismatch means "cannot
|
|
1326
|
+
* restore this field", a missing source means "cannot restore this module at
|
|
1327
|
+
* all", and a rendered string cannot be told apart.
|
|
1328
|
+
*/
|
|
1329
|
+
var HistoryError = z.discriminatedUnion("kind", [z.object({
|
|
1330
|
+
kind: z.literal("commit-not-found"),
|
|
1331
|
+
commitSha: z.string()
|
|
1332
|
+
}), z.object({
|
|
1333
|
+
kind: z.literal("archive-unreadable"),
|
|
1334
|
+
commitSha: z.string(),
|
|
1335
|
+
message: z.string()
|
|
1336
|
+
}), z.object({
|
|
1337
|
+
kind: z.literal("source-unavailable"),
|
|
1338
|
+
moduleFilePath: z.string()
|
|
1339
|
+
}), z.object({
|
|
1340
|
+
kind: z.literal("schema-unreadable"),
|
|
1341
|
+
moduleFilePath: z.string(),
|
|
1342
|
+
message: z.string()
|
|
1343
|
+
}), z.object({
|
|
1344
|
+
kind: z.literal("file-unavailable"),
|
|
1345
|
+
gitPath: z.string(),
|
|
1346
|
+
message: z.string()
|
|
1347
|
+
}), z.object({
|
|
1348
|
+
kind: z.literal("not-supported-in-fs-mode")
|
|
1349
|
+
}), z.object({
|
|
1350
|
+
kind: z.literal("transport"),
|
|
1351
|
+
message: z.string()
|
|
1352
|
+
})]);
|
|
1353
|
+
var HistoricalCommit = z.object({
|
|
1354
|
+
commitSha: z.string(),
|
|
1355
|
+
parentCommitSha: z.string(),
|
|
1356
|
+
clientCommitSha: z.string(),
|
|
1357
|
+
branch: z.string(),
|
|
1358
|
+
createdBranch: z.string().nullable(),
|
|
1359
|
+
creator: z.string().nullable(),
|
|
1360
|
+
message: z.string().nullable(),
|
|
1361
|
+
createdAt: z.string(),
|
|
1362
|
+
seqNum: z.string(),
|
|
1363
|
+
patchCount: z.number(),
|
|
1364
|
+
/** False for commits made before history was recorded. */
|
|
1365
|
+
hasArchive: z["boolean"]()
|
|
1366
|
+
});
|
|
1367
|
+
var FileChange = z.union([z.literal("added"), z.literal("modified"), z.literal("deleted")]);
|
|
1368
|
+
|
|
1369
|
+
/**
|
|
1370
|
+
* A binary file the commit touched - named, with a URL, and nothing fetched.
|
|
1371
|
+
* Only an `<img src>` that mounts pays for the bytes.
|
|
1372
|
+
*/
|
|
1373
|
+
var BinaryFileRef = z.object({
|
|
1374
|
+
gitPath: z.string(),
|
|
1375
|
+
change: FileChange,
|
|
1376
|
+
remote: z["boolean"](),
|
|
1377
|
+
url: z.string()
|
|
1378
|
+
});
|
|
1379
|
+
var HistoricalModule = z.object({
|
|
1380
|
+
/** The module's data at this commit. Null if deleted, or unreadable. */
|
|
1381
|
+
source: JSONValue.nullable(),
|
|
1382
|
+
/**
|
|
1383
|
+
* The schema at this commit, already validated against THIS Val.
|
|
1384
|
+
*
|
|
1385
|
+
* Null with a `schema-unreadable` failure means the stored schema came from a
|
|
1386
|
+
* Val whose schema format this one does not know — expected, not damage.
|
|
1387
|
+
*/
|
|
1388
|
+
schema: SerializedSchema.nullable(),
|
|
1389
|
+
patchIds: z.array(PatchId),
|
|
1390
|
+
changedPaths: z.array(z.string()),
|
|
1391
|
+
failures: z.array(HistoryError)
|
|
1392
|
+
});
|
|
1393
|
+
var HistoricalPatchSet = z.object({
|
|
1394
|
+
commit: HistoricalCommit,
|
|
1395
|
+
modules: z.record(z.string(), HistoricalModule),
|
|
1396
|
+
patches: z.array(z.object({
|
|
1397
|
+
patchId: PatchId,
|
|
1398
|
+
moduleFilePath: z.string(),
|
|
1399
|
+
patch: z.unknown(),
|
|
1400
|
+
authorId: z.string().nullable(),
|
|
1401
|
+
createdAt: z.string(),
|
|
1402
|
+
baseSha: z.string(),
|
|
1403
|
+
coreVersion: z.string()
|
|
1404
|
+
})),
|
|
1405
|
+
jsonEntries: z.record(z.string(), z.unknown()),
|
|
1406
|
+
binaryFiles: z.array(BinaryFileRef),
|
|
1407
|
+
warnings: z.array(HistoryError)
|
|
1408
|
+
});
|
|
1409
|
+
/**
|
|
1410
|
+
* Whether the historical value can be written back as the project stands.
|
|
1411
|
+
*
|
|
1412
|
+
* Computed in the Studio, not on the server: the verdict depends on the current
|
|
1413
|
+
* source and the current schema, both of which the client already holds, and it
|
|
1414
|
+
* runs against the same worker-thread validator that validates ordinary edits.
|
|
1415
|
+
* The types stay here because they are what a restore preview hands to the UI.
|
|
1416
|
+
*
|
|
1417
|
+
* `blocked` carries its reasons so a refusal can name the field and say why,
|
|
1418
|
+
* rather than being an unexplained disabled button.
|
|
1419
|
+
*/
|
|
1420
|
+
var RestoreVerdict = z.union([z.object({
|
|
1421
|
+
status: z.literal("restorable")
|
|
1422
|
+
}), z.object({
|
|
1423
|
+
status: z.literal("blocked"),
|
|
1424
|
+
reasons: z.array(HistoryError)
|
|
1425
|
+
})]);
|
|
1426
|
+
var ModuleComparison = z.object({
|
|
1427
|
+
current: z.unknown().nullable(),
|
|
1428
|
+
/** Paths where the commit's result and the current source differ: what a
|
|
1429
|
+
* restore would undo. */
|
|
1430
|
+
changedVsCurrent: z.array(z.string()),
|
|
1431
|
+
verdict: RestoreVerdict
|
|
1432
|
+
});
|
|
1433
|
+
|
|
1290
1434
|
var ModuleFilePath = z.string().refine(function (_path) {
|
|
1291
1435
|
return true;
|
|
1292
1436
|
} // TODO: validation
|
|
@@ -1556,48 +1700,6 @@ var Api = {
|
|
|
1556
1700
|
})
|
|
1557
1701
|
}
|
|
1558
1702
|
},
|
|
1559
|
-
"/session": {
|
|
1560
|
-
GET: {
|
|
1561
|
-
req: {
|
|
1562
|
-
cookies: {
|
|
1563
|
-
val_session: z.string().optional()
|
|
1564
|
-
}
|
|
1565
|
-
},
|
|
1566
|
-
res: z.union([z.object({
|
|
1567
|
-
status: z.literal(200),
|
|
1568
|
-
json: z.object({
|
|
1569
|
-
mode: z.union([z.literal("local"), z.literal("proxy")]),
|
|
1570
|
-
enabled: z["boolean"]()
|
|
1571
|
-
})
|
|
1572
|
-
}), z.object({
|
|
1573
|
-
status: z.union([
|
|
1574
|
-
// TODO: Remove the ones we don't need.
|
|
1575
|
-
z.literal(400), z.literal(401), z.literal(403), z.literal(404), z.literal(500), z.literal(501)]),
|
|
1576
|
-
json: z.object({
|
|
1577
|
-
message: z.string()
|
|
1578
|
-
})
|
|
1579
|
-
}), z.object({
|
|
1580
|
-
status: z.literal(401),
|
|
1581
|
-
json: z.object({
|
|
1582
|
-
message: z.string(),
|
|
1583
|
-
details: z.union([z.string(), z.object({
|
|
1584
|
-
reason: z.string()
|
|
1585
|
-
}), z.object({
|
|
1586
|
-
sub: z.string(),
|
|
1587
|
-
exp: z.number(),
|
|
1588
|
-
token: z.string(),
|
|
1589
|
-
org: z.string(),
|
|
1590
|
-
project: z.string()
|
|
1591
|
-
})])
|
|
1592
|
-
})
|
|
1593
|
-
}), z.object({
|
|
1594
|
-
status: z.literal(500),
|
|
1595
|
-
json: z.object({
|
|
1596
|
-
message: z.string()
|
|
1597
|
-
})
|
|
1598
|
-
})])
|
|
1599
|
-
}
|
|
1600
|
-
},
|
|
1601
1703
|
"/logout": {
|
|
1602
1704
|
GET: {
|
|
1603
1705
|
req: {
|
|
@@ -2389,6 +2491,144 @@ var Api = {
|
|
|
2389
2491
|
})])
|
|
2390
2492
|
}
|
|
2391
2493
|
},
|
|
2494
|
+
// #region history
|
|
2495
|
+
//
|
|
2496
|
+
// Reading the past. Split across three routes on purpose, because they cache
|
|
2497
|
+
// differently: a reconstructed commit cannot change and is immutable
|
|
2498
|
+
// forever, the comparison against current source changes with every edit, and
|
|
2499
|
+
// a file at a fixed commit is immutable too. Folding them together would mean
|
|
2500
|
+
// the cheapest thing dragging the most expensive one's cache headers down to
|
|
2501
|
+
// no-store.
|
|
2502
|
+
"/history/commits": {
|
|
2503
|
+
GET: {
|
|
2504
|
+
req: {
|
|
2505
|
+
query: {
|
|
2506
|
+
branch: onlyOneStringQueryParam,
|
|
2507
|
+
limit: onlyOneIntQueryParam.optional(),
|
|
2508
|
+
/** The previous page's `nextCursor`. */
|
|
2509
|
+
cursor: onlyOneStringQueryParam.optional()
|
|
2510
|
+
},
|
|
2511
|
+
cookies: {
|
|
2512
|
+
val_session: z.string().optional()
|
|
2513
|
+
}
|
|
2514
|
+
},
|
|
2515
|
+
res: z.union([unauthorizedResponse, notFoundResponse, z.object({
|
|
2516
|
+
status: z.literal(400),
|
|
2517
|
+
json: GenericError.and(z.object({
|
|
2518
|
+
kind: z.string().optional()
|
|
2519
|
+
}))
|
|
2520
|
+
}), z.object({
|
|
2521
|
+
status: z.literal(500),
|
|
2522
|
+
json: GenericError.and(z.object({
|
|
2523
|
+
kind: z.string().optional()
|
|
2524
|
+
}))
|
|
2525
|
+
}), z.object({
|
|
2526
|
+
status: z.literal(200),
|
|
2527
|
+
json: z.object({
|
|
2528
|
+
commits: z.array(HistoricalCommit),
|
|
2529
|
+
nextCursor: z.string().nullable()
|
|
2530
|
+
})
|
|
2531
|
+
})])
|
|
2532
|
+
}
|
|
2533
|
+
},
|
|
2534
|
+
/**
|
|
2535
|
+
* One commit, reconstructed. IMMUTABLE for a given sha - it says nothing
|
|
2536
|
+
* about the current source or schema, which is what lets it be cached
|
|
2537
|
+
* forever and what makes comparing many commits cheap.
|
|
2538
|
+
*/
|
|
2539
|
+
"/history/commit": {
|
|
2540
|
+
GET: {
|
|
2541
|
+
req: {
|
|
2542
|
+
query: {
|
|
2543
|
+
commit_sha: onlyOneStringQueryParam
|
|
2544
|
+
},
|
|
2545
|
+
cookies: {
|
|
2546
|
+
val_session: z.string().optional()
|
|
2547
|
+
}
|
|
2548
|
+
},
|
|
2549
|
+
res: z.union([unauthorizedResponse, notFoundResponse, z.object({
|
|
2550
|
+
status: z.literal(400),
|
|
2551
|
+
json: GenericError
|
|
2552
|
+
}), z.object({
|
|
2553
|
+
status: z.literal(500),
|
|
2554
|
+
json: GenericError.and(z.object({
|
|
2555
|
+
kind: z.string().optional()
|
|
2556
|
+
}))
|
|
2557
|
+
}), z.object({
|
|
2558
|
+
status: z.literal(200),
|
|
2559
|
+
json: HistoricalPatchSet
|
|
2560
|
+
})])
|
|
2561
|
+
}
|
|
2562
|
+
},
|
|
2563
|
+
/**
|
|
2564
|
+
* ONE module, as of a commit — including a commit that did not change it.
|
|
2565
|
+
*
|
|
2566
|
+
* `/history/commit` covers what a commit changed, which is the narrow case:
|
|
2567
|
+
* navigate the history pane to anything else and there would be nothing to
|
|
2568
|
+
* show. This answers "how did this module look at that point", which is the
|
|
2569
|
+
* question someone comparing two panes is actually asking. Immutable for the
|
|
2570
|
+
* same reason: what a commit left behind cannot change.
|
|
2571
|
+
*/
|
|
2572
|
+
"/history/module": {
|
|
2573
|
+
GET: {
|
|
2574
|
+
req: {
|
|
2575
|
+
query: {
|
|
2576
|
+
commit_sha: onlyOneStringQueryParam,
|
|
2577
|
+
module_file_path: onlyOneStringQueryParam
|
|
2578
|
+
},
|
|
2579
|
+
cookies: {
|
|
2580
|
+
val_session: z.string().optional()
|
|
2581
|
+
}
|
|
2582
|
+
},
|
|
2583
|
+
res: z.union([unauthorizedResponse, notFoundResponse, z.object({
|
|
2584
|
+
status: z.literal(400),
|
|
2585
|
+
json: GenericError
|
|
2586
|
+
}), z.object({
|
|
2587
|
+
status: z.literal(500),
|
|
2588
|
+
json: GenericError.and(z.object({
|
|
2589
|
+
kind: z.string().optional()
|
|
2590
|
+
}))
|
|
2591
|
+
}), z.object({
|
|
2592
|
+
status: z.literal(200),
|
|
2593
|
+
json: z.object({
|
|
2594
|
+
moduleFilePath: z.string(),
|
|
2595
|
+
/**
|
|
2596
|
+
* Null when history has no record of this module at or before the
|
|
2597
|
+
* commit — it was last edited before recording started, or never.
|
|
2598
|
+
* Distinct from a module the commit deleted.
|
|
2599
|
+
*/
|
|
2600
|
+
module: HistoricalModule.nullable()
|
|
2601
|
+
})
|
|
2602
|
+
})])
|
|
2603
|
+
}
|
|
2604
|
+
},
|
|
2605
|
+
/**
|
|
2606
|
+
* One file as it was at one commit.
|
|
2607
|
+
*
|
|
2608
|
+
* The lazy half: `/history/commit` NAMES a commit's binary files and this is
|
|
2609
|
+
* where their bytes come from, so nothing is fetched until an `<img>`
|
|
2610
|
+
* actually mounts. A file at a fixed commit cannot change, so the response is
|
|
2611
|
+
* immutable and the browser's own cache handles flipping between commits.
|
|
2612
|
+
*/
|
|
2613
|
+
"/history/files": {
|
|
2614
|
+
GET: {
|
|
2615
|
+
req: {
|
|
2616
|
+
query: {
|
|
2617
|
+
commit_sha: onlyOneStringQueryParam,
|
|
2618
|
+
path: onlyOneStringQueryParam,
|
|
2619
|
+
remote: onlyOneStringQueryParam.optional()
|
|
2620
|
+
}
|
|
2621
|
+
},
|
|
2622
|
+
res: z.union([unauthorizedResponse, notFoundResponse, z.object({
|
|
2623
|
+
status: z.literal(400),
|
|
2624
|
+
json: GenericError
|
|
2625
|
+
}), z.object({
|
|
2626
|
+
status: z.literal(200),
|
|
2627
|
+
body: z["instanceof"](ReadableStream)
|
|
2628
|
+
})])
|
|
2629
|
+
}
|
|
2630
|
+
},
|
|
2631
|
+
// #endregion history
|
|
2392
2632
|
"/files": {
|
|
2393
2633
|
GET: {
|
|
2394
2634
|
req: {
|
|
@@ -3025,6 +3265,354 @@ var urlOf = function urlOf() {
|
|
|
3025
3265
|
return "".concat(route);
|
|
3026
3266
|
};
|
|
3027
3267
|
|
|
3268
|
+
/**
|
|
3269
|
+
* Can this value, taken from one schema, be written into another?
|
|
3270
|
+
*
|
|
3271
|
+
* The question restore asks, and it is NOT the question validation asks.
|
|
3272
|
+
* Validation checks a value against the schema it belongs to. This compares two
|
|
3273
|
+
* schemas — the one a value was stored under and the one it is being written
|
|
3274
|
+
* into — because a restore moves a value across a schema change, and possibly
|
|
3275
|
+
* across paths.
|
|
3276
|
+
*
|
|
3277
|
+
* Three answers, not two. `unknown` is where the comparison genuinely cannot
|
|
3278
|
+
* decide, and it exists so that an editor is told "we are not sure" rather than
|
|
3279
|
+
* being given a confident answer we made up. Treating unknown as compatible
|
|
3280
|
+
* writes values that break; treating it as incompatible blocks restores that
|
|
3281
|
+
* would have been fine.
|
|
3282
|
+
*/
|
|
3283
|
+
|
|
3284
|
+
var compatible = {
|
|
3285
|
+
status: "compatible"
|
|
3286
|
+
};
|
|
3287
|
+
|
|
3288
|
+
/**
|
|
3289
|
+
* `Array.isArray` cannot narrow `JsonArray` out of `Json`, because it is
|
|
3290
|
+
* `readonly Json[]` — so the check that reads naturally does not type. This
|
|
3291
|
+
* predicate is that check, said once.
|
|
3292
|
+
*/
|
|
3293
|
+
function isJsonObject(value) {
|
|
3294
|
+
return _typeof(value) === "object" && value !== null && !Array.isArray(value);
|
|
3295
|
+
}
|
|
3296
|
+
function incompatible(reason) {
|
|
3297
|
+
return {
|
|
3298
|
+
status: "incompatible",
|
|
3299
|
+
reason: reason
|
|
3300
|
+
};
|
|
3301
|
+
}
|
|
3302
|
+
|
|
3303
|
+
/**
|
|
3304
|
+
* "an object", "a string".
|
|
3305
|
+
*
|
|
3306
|
+
* Schema type names are data — `object`, `array`, `image` — so an interpolated
|
|
3307
|
+
* "a" is wrong for three of them, in a sentence shown to editors the moment a
|
|
3308
|
+
* restore is refused.
|
|
3309
|
+
*/
|
|
3310
|
+
function withArticle(type) {
|
|
3311
|
+
return /^[aeiou]/i.test(type) ? "an ".concat(type) : "a ".concat(type);
|
|
3312
|
+
}
|
|
3313
|
+
|
|
3314
|
+
/**
|
|
3315
|
+
* A sentence an editor can act on, for the popup that explains a refusal.
|
|
3316
|
+
*
|
|
3317
|
+
* Kept beside the reasons rather than in the UI so that adding a reason without
|
|
3318
|
+
* wording for it is a type error rather than a blank dialog.
|
|
3319
|
+
*/
|
|
3320
|
+
function explainIncompatible(reason) {
|
|
3321
|
+
switch (reason.kind) {
|
|
3322
|
+
case "type-changed":
|
|
3323
|
+
return "This was ".concat(withArticle(reason.from), " and the field here is ").concat(withArticle(reason.to), ".");
|
|
3324
|
+
case "no-matching-variant":
|
|
3325
|
+
return "This does not match any of the shapes allowed here: ".concat(reason.variants.join(", "), ".");
|
|
3326
|
+
case "unknown-field":
|
|
3327
|
+
return "The old value has ".concat(reason.keys.length === 1 ? "a field" : "fields", " that no longer exist here: ").concat(reason.keys.join(", "), ".");
|
|
3328
|
+
case "missing-required":
|
|
3329
|
+
return "The field here needs ".concat(reason.keys.join(", "), ", which the old value does not have.");
|
|
3330
|
+
case "not-optional":
|
|
3331
|
+
return "The old value was empty, and this field cannot be empty.";
|
|
3332
|
+
case "literal-changed":
|
|
3333
|
+
return "This has to be exactly \"".concat(reason.to, "\", and the old value was \"").concat(reason.from, "\".");
|
|
3334
|
+
case "different-module":
|
|
3335
|
+
return "This points into ".concat(reason.to, ", and the old value pointed into ").concat(reason.from, ".");
|
|
3336
|
+
}
|
|
3337
|
+
}
|
|
3338
|
+
|
|
3339
|
+
/**
|
|
3340
|
+
* The variant of a union that a value actually is.
|
|
3341
|
+
*
|
|
3342
|
+
* `undefined` when the value does not identify itself — which is a real answer,
|
|
3343
|
+
* not a failure: it is what makes the caller return `unknown` rather than guess.
|
|
3344
|
+
*/
|
|
3345
|
+
function variantOf(schema, value) {
|
|
3346
|
+
if (schema.type !== "union") {
|
|
3347
|
+
return undefined;
|
|
3348
|
+
}
|
|
3349
|
+
// String union: the value IS the discriminator.
|
|
3350
|
+
if (typeof schema.key !== "string") {
|
|
3351
|
+
if (typeof value !== "string") {
|
|
3352
|
+
return undefined;
|
|
3353
|
+
}
|
|
3354
|
+
return schema.items.find(function (item) {
|
|
3355
|
+
return item.type === "literal" && item.value === value;
|
|
3356
|
+
});
|
|
3357
|
+
}
|
|
3358
|
+
// Object union: the discriminator is a literal at `key`.
|
|
3359
|
+
var discriminator = schema.key;
|
|
3360
|
+
if (!isJsonObject(value)) {
|
|
3361
|
+
return undefined;
|
|
3362
|
+
}
|
|
3363
|
+
var tag = value[discriminator];
|
|
3364
|
+
if (typeof tag !== "string") {
|
|
3365
|
+
return undefined;
|
|
3366
|
+
}
|
|
3367
|
+
return schema.items.find(function (item) {
|
|
3368
|
+
if (item.type !== "object") {
|
|
3369
|
+
return false;
|
|
3370
|
+
}
|
|
3371
|
+
var keySchema = item.items[discriminator];
|
|
3372
|
+
return (keySchema === null || keySchema === void 0 ? void 0 : keySchema.type) === "literal" && keySchema.value === tag;
|
|
3373
|
+
});
|
|
3374
|
+
}
|
|
3375
|
+
function describeVariant(schema, key) {
|
|
3376
|
+
if (schema.type === "literal") {
|
|
3377
|
+
return schema.value;
|
|
3378
|
+
}
|
|
3379
|
+
if (schema.type === "object") {
|
|
3380
|
+
var tag = schema.items[key];
|
|
3381
|
+
if ((tag === null || tag === void 0 ? void 0 : tag.type) === "literal") {
|
|
3382
|
+
return tag.value;
|
|
3383
|
+
}
|
|
3384
|
+
}
|
|
3385
|
+
return schema.type;
|
|
3386
|
+
}
|
|
3387
|
+
function checkCompatibility(from, to) {
|
|
3388
|
+
// Emptiness first: a null that lands in a field which cannot be empty is a
|
|
3389
|
+
// break regardless of what the two schemas otherwise say.
|
|
3390
|
+
if (from.value === null) {
|
|
3391
|
+
return to.schema.opt ? compatible : incompatible({
|
|
3392
|
+
kind: "not-optional"
|
|
3393
|
+
});
|
|
3394
|
+
}
|
|
3395
|
+
|
|
3396
|
+
// A changed union is not itself disqualifying. What matters is whether the
|
|
3397
|
+
// variant being restored still has somewhere to go — comparing the unions
|
|
3398
|
+
// wholesale would refuse a restore that is perfectly safe.
|
|
3399
|
+
if (to.schema.type === "union") {
|
|
3400
|
+
var key = typeof to.schema.key === "string" ? to.schema.key : "";
|
|
3401
|
+
var _iterator = _createForOfIteratorHelper(to.schema.items),
|
|
3402
|
+
_step;
|
|
3403
|
+
try {
|
|
3404
|
+
for (_iterator.s(); !(_step = _iterator.n()).done;) {
|
|
3405
|
+
var variant = _step.value;
|
|
3406
|
+
if (checkCompatibility(from, {
|
|
3407
|
+
schema: variant
|
|
3408
|
+
}).status === "compatible") {
|
|
3409
|
+
return compatible;
|
|
3410
|
+
}
|
|
3411
|
+
}
|
|
3412
|
+
} catch (err) {
|
|
3413
|
+
_iterator.e(err);
|
|
3414
|
+
} finally {
|
|
3415
|
+
_iterator.f();
|
|
3416
|
+
}
|
|
3417
|
+
return incompatible({
|
|
3418
|
+
kind: "no-matching-variant",
|
|
3419
|
+
variants: to.schema.items.map(function (item) {
|
|
3420
|
+
return describeVariant(item, key);
|
|
3421
|
+
})
|
|
3422
|
+
});
|
|
3423
|
+
}
|
|
3424
|
+
|
|
3425
|
+
// Coming FROM a union into something that is not one: only the variant this
|
|
3426
|
+
// value actually is matters, so narrow before comparing.
|
|
3427
|
+
if (from.schema.type === "union") {
|
|
3428
|
+
var _variant = variantOf(from.schema, from.value);
|
|
3429
|
+
if (_variant === undefined) {
|
|
3430
|
+
return {
|
|
3431
|
+
status: "unknown",
|
|
3432
|
+
message: "The old value does not say which shape it is, so we cannot tell whether it fits here."
|
|
3433
|
+
};
|
|
3434
|
+
}
|
|
3435
|
+
return checkCompatibility({
|
|
3436
|
+
schema: _variant,
|
|
3437
|
+
value: from.value
|
|
3438
|
+
}, to);
|
|
3439
|
+
}
|
|
3440
|
+
if (from.schema.type !== to.schema.type) {
|
|
3441
|
+
return incompatible({
|
|
3442
|
+
kind: "type-changed",
|
|
3443
|
+
from: from.schema.type,
|
|
3444
|
+
to: to.schema.type
|
|
3445
|
+
});
|
|
3446
|
+
}
|
|
3447
|
+
switch (to.schema.type) {
|
|
3448
|
+
case "literal":
|
|
3449
|
+
{
|
|
3450
|
+
var fromValue = from.schema.type === "literal" ? from.schema.value : String(from.value);
|
|
3451
|
+
return fromValue === to.schema.value ? compatible : incompatible({
|
|
3452
|
+
kind: "literal-changed",
|
|
3453
|
+
from: fromValue,
|
|
3454
|
+
to: to.schema.value
|
|
3455
|
+
});
|
|
3456
|
+
}
|
|
3457
|
+
case "object":
|
|
3458
|
+
{
|
|
3459
|
+
if (from.schema.type !== "object" || !isJsonObject(from.value)) {
|
|
3460
|
+
return {
|
|
3461
|
+
status: "unknown",
|
|
3462
|
+
message: "The old value is not shaped like an object."
|
|
3463
|
+
};
|
|
3464
|
+
}
|
|
3465
|
+
var _fromValue = from.value;
|
|
3466
|
+
var target = to.schema.items;
|
|
3467
|
+
var source = from.schema.items;
|
|
3468
|
+
|
|
3469
|
+
// Keys the old value carries that the field no longer has. Writing them
|
|
3470
|
+
// would put data into a schema with nowhere to hold it.
|
|
3471
|
+
var extra = Object.keys(source).filter(function (key) {
|
|
3472
|
+
return !(key in target);
|
|
3473
|
+
});
|
|
3474
|
+
if (extra.length > 0) {
|
|
3475
|
+
return incompatible({
|
|
3476
|
+
kind: "unknown-field",
|
|
3477
|
+
keys: extra
|
|
3478
|
+
});
|
|
3479
|
+
}
|
|
3480
|
+
|
|
3481
|
+
// Keys the field requires that the old value cannot supply.
|
|
3482
|
+
var missing = Object.keys(target).filter(function (key) {
|
|
3483
|
+
return !target[key].opt && !(key in source);
|
|
3484
|
+
});
|
|
3485
|
+
if (missing.length > 0) {
|
|
3486
|
+
return incompatible({
|
|
3487
|
+
kind: "missing-required",
|
|
3488
|
+
keys: missing
|
|
3489
|
+
});
|
|
3490
|
+
}
|
|
3491
|
+
var unknown = null;
|
|
3492
|
+
for (var _i = 0, _Object$entries = Object.entries(target); _i < _Object$entries.length; _i++) {
|
|
3493
|
+
var _fromValue$_key;
|
|
3494
|
+
var _Object$entries$_i = _slicedToArray(_Object$entries[_i], 2),
|
|
3495
|
+
_key = _Object$entries$_i[0],
|
|
3496
|
+
targetItem = _Object$entries$_i[1];
|
|
3497
|
+
var sourceItem = source[_key];
|
|
3498
|
+
if (sourceItem === undefined) {
|
|
3499
|
+
continue; // optional and absent — already allowed above
|
|
3500
|
+
}
|
|
3501
|
+
var res = checkCompatibility({
|
|
3502
|
+
schema: sourceItem,
|
|
3503
|
+
value: (_fromValue$_key = _fromValue[_key]) !== null && _fromValue$_key !== void 0 ? _fromValue$_key : null
|
|
3504
|
+
}, {
|
|
3505
|
+
schema: targetItem
|
|
3506
|
+
});
|
|
3507
|
+
if (res.status === "incompatible") {
|
|
3508
|
+
return res;
|
|
3509
|
+
}
|
|
3510
|
+
if (res.status === "unknown" && unknown === null) {
|
|
3511
|
+
unknown = res;
|
|
3512
|
+
}
|
|
3513
|
+
}
|
|
3514
|
+
return unknown !== null && unknown !== void 0 ? unknown : compatible;
|
|
3515
|
+
}
|
|
3516
|
+
case "array":
|
|
3517
|
+
{
|
|
3518
|
+
if (from.schema.type !== "array" || !Array.isArray(from.value)) {
|
|
3519
|
+
return {
|
|
3520
|
+
status: "unknown",
|
|
3521
|
+
message: "The old value is not a list."
|
|
3522
|
+
};
|
|
3523
|
+
}
|
|
3524
|
+
// An empty list has nothing to probe the item schema with. The item
|
|
3525
|
+
// TYPES still have to agree, and that much can be decided without a
|
|
3526
|
+
// value - so decide it rather than passing an empty list as proof.
|
|
3527
|
+
if (from.value.length === 0) {
|
|
3528
|
+
return from.schema.item.type === to.schema.item.type ? compatible : incompatible({
|
|
3529
|
+
kind: "type-changed",
|
|
3530
|
+
from: from.schema.item.type,
|
|
3531
|
+
to: to.schema.item.type
|
|
3532
|
+
});
|
|
3533
|
+
}
|
|
3534
|
+
var _unknown = null;
|
|
3535
|
+
var _iterator2 = _createForOfIteratorHelper(from.value),
|
|
3536
|
+
_step2;
|
|
3537
|
+
try {
|
|
3538
|
+
for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
|
|
3539
|
+
var item = _step2.value;
|
|
3540
|
+
var _res = checkCompatibility({
|
|
3541
|
+
schema: from.schema.item,
|
|
3542
|
+
value: item
|
|
3543
|
+
}, {
|
|
3544
|
+
schema: to.schema.item
|
|
3545
|
+
});
|
|
3546
|
+
if (_res.status === "incompatible") {
|
|
3547
|
+
return _res;
|
|
3548
|
+
}
|
|
3549
|
+
if (_res.status === "unknown" && _unknown === null) {
|
|
3550
|
+
_unknown = _res;
|
|
3551
|
+
}
|
|
3552
|
+
}
|
|
3553
|
+
} catch (err) {
|
|
3554
|
+
_iterator2.e(err);
|
|
3555
|
+
} finally {
|
|
3556
|
+
_iterator2.f();
|
|
3557
|
+
}
|
|
3558
|
+
return _unknown !== null && _unknown !== void 0 ? _unknown : compatible;
|
|
3559
|
+
}
|
|
3560
|
+
case "record":
|
|
3561
|
+
{
|
|
3562
|
+
if (from.schema.type !== "record" || !isJsonObject(from.value)) {
|
|
3563
|
+
return {
|
|
3564
|
+
status: "unknown",
|
|
3565
|
+
message: "The old value is not a record."
|
|
3566
|
+
};
|
|
3567
|
+
}
|
|
3568
|
+
var _unknown2 = null;
|
|
3569
|
+
for (var _i2 = 0, _Object$values = Object.values(from.value); _i2 < _Object$values.length; _i2++) {
|
|
3570
|
+
var entry = _Object$values[_i2];
|
|
3571
|
+
var _res2 = checkCompatibility({
|
|
3572
|
+
schema: from.schema.item,
|
|
3573
|
+
value: entry !== null && entry !== void 0 ? entry : null
|
|
3574
|
+
}, {
|
|
3575
|
+
schema: to.schema.item
|
|
3576
|
+
});
|
|
3577
|
+
if (_res2.status === "incompatible") {
|
|
3578
|
+
return _res2;
|
|
3579
|
+
}
|
|
3580
|
+
if (_res2.status === "unknown" && _unknown2 === null) {
|
|
3581
|
+
_unknown2 = _res2;
|
|
3582
|
+
}
|
|
3583
|
+
}
|
|
3584
|
+
return _unknown2 !== null && _unknown2 !== void 0 ? _unknown2 : compatible;
|
|
3585
|
+
}
|
|
3586
|
+
case "keyOf":
|
|
3587
|
+
{
|
|
3588
|
+
// A key is only meaningful against the module it indexes.
|
|
3589
|
+
if (from.schema.type !== "keyOf") {
|
|
3590
|
+
return compatible;
|
|
3591
|
+
}
|
|
3592
|
+
return from.schema.path === to.schema.path ? compatible : incompatible({
|
|
3593
|
+
kind: "different-module",
|
|
3594
|
+
from: from.schema.path,
|
|
3595
|
+
to: to.schema.path
|
|
3596
|
+
});
|
|
3597
|
+
}
|
|
3598
|
+
case "richtext":
|
|
3599
|
+
{
|
|
3600
|
+
// The options decide which nodes are legal, and comparing them properly
|
|
3601
|
+
// means comparing node trees against them. Not yet done, and saying so is
|
|
3602
|
+
// better than a confident answer we cannot back.
|
|
3603
|
+
return {
|
|
3604
|
+
status: "unknown",
|
|
3605
|
+
message: "Rich text can be restored, but we cannot yet confirm every mark and block is still allowed here."
|
|
3606
|
+
};
|
|
3607
|
+
}
|
|
3608
|
+
default:
|
|
3609
|
+
// Primitives and media: same type is the whole test. Constraints
|
|
3610
|
+
// (maxLength, accept, …) are validated on write, which reports them
|
|
3611
|
+
// better than a schema comparison could.
|
|
3612
|
+
return compatible;
|
|
3613
|
+
}
|
|
3614
|
+
}
|
|
3615
|
+
|
|
3028
3616
|
/**
|
|
3029
3617
|
* The newest commit the client has been told about, or `null` if none.
|
|
3030
3618
|
*
|
|
@@ -4530,6 +5118,13 @@ function isSkippableFixCode(fix) {
|
|
|
4530
5118
|
// rewriting the module) and saving does not repair it either, so surface
|
|
4531
5119
|
// it: the message names the command (`val validate --fix`) that does.
|
|
4532
5120
|
return false;
|
|
5121
|
+
case "external:upload":
|
|
5122
|
+
// Same shape as `jsonValues:extract-entry` — an entry inlined in a
|
|
5123
|
+
// `.val.ts` that belongs elsewhere — and more emphatically not skippable.
|
|
5124
|
+
// Fixing it writes to a live store, which is why even the CLI does it only
|
|
5125
|
+
// under `val external upload` and never under a blanket `--fix`. Surfacing
|
|
5126
|
+
// it is the whole point: someone has to decide.
|
|
5127
|
+
return false;
|
|
4533
5128
|
default:
|
|
4534
5129
|
{
|
|
4535
5130
|
return false;
|
|
@@ -4569,4 +5164,4 @@ function filterBlockingValidationErrors(validationErrors, schemas, sources) {
|
|
|
4569
5164
|
return partitionValidationErrors(resolved).surfaced;
|
|
4570
5165
|
}
|
|
4571
5166
|
|
|
4572
|
-
export { Api, CodeOptions, ColorOptions, DEFAULT_ERROR_MESSAGE, DateOptions, FileOptions, ImageEncodeOption, ImageOptions, JSON_ENTRIES_BATCH_MAX, ParentRef$1 as ParentRef, Patch, PatchBlock, PatchGroup, PatchId, PatchJSON, RemirrorBr, RemirrorBulletList, RemirrorHeading, RemirrorImage, RemirrorJSON, RemirrorLinkMark, RemirrorListItem, RemirrorOrderedList, RemirrorParagraph, RemirrorText, RemirrorTextMark, RichTextOptions, SerializedArraySchema, SerializedBooleanSchema, SerializedCodeSchema, SerializedColorSchema, SerializedDateSchema, SerializedDateTimeSchema, SerializedFileSchema, SerializedImageSchema, SerializedKeyOfSchema, SerializedLiteralSchema, SerializedNumberSchema, SerializedObjectSchema, SerializedRecordSchema, SerializedRichTextSchema, SerializedRouteSchema, SerializedSchema, SerializedStringSchema, SerializedUnionSchema, SharedValConfig, VAL_AI_SESSION_STORAGE_KEY, VAL_CANVAS_MESSAGE, VAL_CANVAS_PARAM, VAL_CONFIG_SESSION_STORAGE_KEY, VAL_ENABLE_COOKIE_NAME, VAL_SESSION_COOKIE, VAL_STATE_COOKIE, VAL_THEME_SESSION_STORAGE_KEY, ValCommit, ValDeployment, buildDuplicatePatch, buildEmptyAtPathPatch, buildFileRefValue, buildImageGalleryPatch, buildRemoveImageGalleryEntryPatch, createRegExpFromPattern, createValClient, describeContainerAtPath, emptyOf, filterBlockingValidationErrors, filterRoutesByPatterns, findSimilar, getErrorMessageFromUnknownJson, getInlineImgInfo, getNextAppRouterSitemapTree, getNextAppRouterSourceFolder, getPatternFromModuleFilePath, getRoutesOf, getRoutesWithModulePaths, getSourceAt, isRemoteSchema, isValCanvasFrame, isValCanvasPageMessage, isValCanvasStudioMessage, levenshtein, newestCommitSha, parseRoutePattern, partitionValidationErrors, remirrorToRichTextSource, resolveSchemaSourceFixForError, resolveSchemaSourceFixes, resolveSerializedSchemaAtPath, richTextToRemirror, safeParsePatch, toolNames, urlOf, validateRoutePatterns, withValCanvasParam };
|
|
5167
|
+
export { Api, BinaryFileRef, CodeOptions, ColorOptions, DEFAULT_ERROR_MESSAGE, DateOptions, FileOptions, HistoricalCommit, HistoricalModule, HistoricalPatchSet, HistoryError, ImageEncodeOption, ImageOptions, JSONValue, JSON_ENTRIES_BATCH_MAX, ModuleComparison, ParentRef$1 as ParentRef, Patch, PatchBlock, PatchGroup, PatchId, PatchJSON, RemirrorBr, RemirrorBulletList, RemirrorHeading, RemirrorImage, RemirrorJSON, RemirrorLinkMark, RemirrorListItem, RemirrorOrderedList, RemirrorParagraph, RemirrorText, RemirrorTextMark, RestoreVerdict, RichTextOptions, SerializedArraySchema, SerializedBooleanSchema, SerializedCodeSchema, SerializedColorSchema, SerializedDateSchema, SerializedDateTimeSchema, SerializedFileSchema, SerializedImageSchema, SerializedKeyOfSchema, SerializedLiteralSchema, SerializedNumberSchema, SerializedObjectSchema, SerializedRecordSchema, SerializedRichTextSchema, SerializedRouteSchema, SerializedSchema, SerializedSettingsSchema, SerializedStringSchema, SerializedUnionSchema, SharedValConfig, VAL_AI_SESSION_STORAGE_KEY, VAL_CANVAS_MESSAGE, VAL_CANVAS_PARAM, VAL_CONFIG_SESSION_STORAGE_KEY, VAL_ENABLE_COOKIE_NAME, VAL_SESSION_COOKIE, VAL_STATE_COOKIE, VAL_THEME_SESSION_STORAGE_KEY, ValCommit, ValDeployment, buildDuplicatePatch, buildEmptyAtPathPatch, buildFileRefValue, buildImageGalleryPatch, buildRemoveImageGalleryEntryPatch, checkCompatibility, createRegExpFromPattern, createValClient, describeContainerAtPath, emptyOf, explainIncompatible, filterBlockingValidationErrors, filterRoutesByPatterns, findSimilar, getErrorMessageFromUnknownJson, getInlineImgInfo, getNextAppRouterSitemapTree, getNextAppRouterSourceFolder, getPatternFromModuleFilePath, getRoutesOf, getRoutesWithModulePaths, getSourceAt, isRemoteSchema, isValCanvasFrame, isValCanvasPageMessage, isValCanvasStudioMessage, levenshtein, newestCommitSha, parseRoutePattern, partitionValidationErrors, remirrorToRichTextSource, resolveSchemaSourceFixForError, resolveSchemaSourceFixes, resolveSerializedSchemaAtPath, richTextToRemirror, safeParsePatch, toolNames, urlOf, validateRoutePatterns, withValCanvasParam };
|