@warmhub/sdk-ts 0.60.1 → 0.61.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/dist/{chunk-D5A5SSUU.js → chunk-QS6ABKPJ.js} +1192 -1147
- package/dist/chunk-QS6ABKPJ.js.map +1 -0
- package/dist/index.d.ts +78 -56
- package/dist/index.js +1 -1
- package/dist/react.d.ts +11 -1
- package/dist/react.js +9 -5
- package/dist/react.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-D5A5SSUU.js.map +0 -1
|
@@ -17,6 +17,13 @@ function isBuiltinShape(name) {
|
|
|
17
17
|
return BUILTIN_SHAPE_NAMES.includes(name) || BUILTIN_CONTENT_SHAPE_NAMES.includes(name);
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
+
// ../rules/src/client-header.ts
|
|
21
|
+
var CLIENT_HEADER = "X-WarmHub-Client";
|
|
22
|
+
var WARMHUB_SDK_CLIENT_NAME = "@warmhub/sdk-ts";
|
|
23
|
+
function formatClientHeader(name, version) {
|
|
24
|
+
return `${name}/${version}`;
|
|
25
|
+
}
|
|
26
|
+
|
|
20
27
|
// ../rules/src/commit-stream-limits.ts
|
|
21
28
|
var DEFAULT_STREAM_APPEND_CHUNK_SIZE = 1e3;
|
|
22
29
|
var MAX_STREAM_APPEND_OPERATION_COUNT = 1e4;
|
|
@@ -190,7 +197,7 @@ var REPO_AUTH_SCOPES = [
|
|
|
190
197
|
"repo:action-callback"
|
|
191
198
|
];
|
|
192
199
|
|
|
193
|
-
// ../rules/src/component-manifest-contract.ts
|
|
200
|
+
// ../rules/src/component-manifest-contract-internals.ts
|
|
194
201
|
new Set(REPO_AUTH_SCOPES);
|
|
195
202
|
|
|
196
203
|
// ../rules/src/field-name-safety.ts
|
|
@@ -378,183 +385,13 @@ function hasAnyTokens(s) {
|
|
|
378
385
|
return ANY_TOKEN_RE.test(s);
|
|
379
386
|
}
|
|
380
387
|
|
|
381
|
-
// ../rules/src/preflight.ts
|
|
382
|
-
var collectionTypes = ["pair", "triple", "set", "list"];
|
|
383
|
-
var collectionOps = ["add", "revise"];
|
|
384
|
-
function preflightOpDiagnostics(op, operationIndex) {
|
|
385
|
-
const errors = [];
|
|
386
|
-
errors.push(...builtinShapeGuard(op, operationIndex));
|
|
387
|
-
errors.push(...contentNameGuard(op, operationIndex));
|
|
388
|
-
errors.push(...plusSignGuard(op, operationIndex));
|
|
389
|
-
errors.push(...validateCollectionAbouts(op, operationIndex));
|
|
390
|
-
errors.push(...validateCollectionOps(op, operationIndex));
|
|
391
|
-
return errors;
|
|
392
|
-
}
|
|
393
|
-
function builtinShapeGuard(op, operationIndex) {
|
|
394
|
-
const errors = [];
|
|
395
|
-
const name = op.name;
|
|
396
|
-
if (op.kind === "shape" && name && isBuiltinShape(name)) {
|
|
397
|
-
errors.push({
|
|
398
|
-
code: "RESERVED_NAME",
|
|
399
|
-
operationIndex,
|
|
400
|
-
message: `Shape "${name}" is a built-in shape and cannot be ${op.operation === "add" ? "created" : "revised"} manually`
|
|
401
|
-
});
|
|
402
|
-
}
|
|
403
|
-
if (op.kind === "thing" && name) {
|
|
404
|
-
const local = splitLocalPath(name);
|
|
405
|
-
if (local && isBuiltinCollectionShape(local.shapePrefix)) {
|
|
406
|
-
const message = op.operation === "add" ? `Cannot add kind="thing" under built-in shape "${local.shapePrefix}". Use kind="collection" or the about sugar instead.` : `Cannot revise kind="thing" under built-in shape "${local.shapePrefix}". Collections are immutable.`;
|
|
407
|
-
errors.push({
|
|
408
|
-
code: "VALIDATION_ERROR",
|
|
409
|
-
operationIndex,
|
|
410
|
-
message
|
|
411
|
-
});
|
|
412
|
-
}
|
|
413
|
-
}
|
|
414
|
-
return errors;
|
|
415
|
-
}
|
|
416
|
-
function contentNameGuard(op, operationIndex) {
|
|
417
|
-
if (op.kind !== "thing" || !op.name) return [];
|
|
418
|
-
const local = splitLocalPath(op.name);
|
|
419
|
-
if (!local || !isContentShape(local.shapePrefix)) return [];
|
|
420
|
-
const bareName = local.bareName;
|
|
421
|
-
if (isSynthesizedContentName(bareName)) {
|
|
422
|
-
return [
|
|
423
|
-
{
|
|
424
|
-
code: "READ_ONLY_BUILTIN_CONTENT",
|
|
425
|
-
operationIndex,
|
|
426
|
-
message: `${local.shapePrefix}/${bareName} is synthesized; writes are not allowed`
|
|
427
|
-
}
|
|
428
|
-
];
|
|
429
|
-
}
|
|
430
|
-
if (!isStoredContentName(bareName)) {
|
|
431
|
-
return [
|
|
432
|
-
{
|
|
433
|
-
code: "UNKNOWN_CONTENT_NAME",
|
|
434
|
-
operationIndex,
|
|
435
|
-
message: `${local.shapePrefix}/${bareName} is not a recognized well-known content name`
|
|
436
|
-
}
|
|
437
|
-
];
|
|
438
|
-
}
|
|
439
|
-
return [];
|
|
440
|
-
}
|
|
441
|
-
function plusSignGuard(op, operationIndex) {
|
|
442
|
-
const errors = [];
|
|
443
|
-
const name = op.name;
|
|
444
|
-
if (op.kind === "collection") return errors;
|
|
445
|
-
if (name?.includes("+")) {
|
|
446
|
-
const local = splitLocalPath(name);
|
|
447
|
-
if (local && isBuiltinCollectionShape(local.shapePrefix)) return errors;
|
|
448
|
-
errors.push({
|
|
449
|
-
code: "VALIDATION_ERROR",
|
|
450
|
-
operationIndex,
|
|
451
|
-
message: `Name "${name}" contains reserved character "+". The "+" character is reserved for collection names.`
|
|
452
|
-
});
|
|
453
|
-
}
|
|
454
|
-
return errors;
|
|
455
|
-
}
|
|
456
|
-
function validateCollectionAbouts(op, operationIndex) {
|
|
457
|
-
const errors = [];
|
|
458
|
-
if (!op.about || typeof op.about === "string") return errors;
|
|
459
|
-
if (!isCollectionAbout(op.about)) {
|
|
460
|
-
const keys = typeof op.about === "object" && op.about !== null ? Object.keys(op.about) : [];
|
|
461
|
-
errors.push({
|
|
462
|
-
code: "VALIDATION_ERROR",
|
|
463
|
-
operationIndex,
|
|
464
|
-
message: `Structured about must have exactly one key: pair, triple, set, or list. Got: ${keys.join(", ") || typeof op.about}`
|
|
465
|
-
});
|
|
466
|
-
return errors;
|
|
467
|
-
}
|
|
468
|
-
const tag = collectionAboutType(op.about);
|
|
469
|
-
const members = collectionAboutMembers(op.about);
|
|
470
|
-
for (let i = 0; i < members.length; i++) {
|
|
471
|
-
if (typeof members[i] !== "string") {
|
|
472
|
-
errors.push({
|
|
473
|
-
code: "VALIDATION_ERROR",
|
|
474
|
-
operationIndex,
|
|
475
|
-
message: `Collection member at index ${i} must be a string, got ${typeof members[i]}`
|
|
476
|
-
});
|
|
477
|
-
}
|
|
478
|
-
}
|
|
479
|
-
const arityError = collectionArityError(tag, members);
|
|
480
|
-
if (arityError) {
|
|
481
|
-
errors.push({
|
|
482
|
-
code: "VALIDATION_ERROR",
|
|
483
|
-
operationIndex,
|
|
484
|
-
message: arityError
|
|
485
|
-
});
|
|
486
|
-
}
|
|
487
|
-
return errors;
|
|
488
|
-
}
|
|
489
|
-
function validateCollectionOps(op, operationIndex) {
|
|
490
|
-
const errors = [];
|
|
491
|
-
if (op.kind !== "collection") return errors;
|
|
492
|
-
if (!collectionOps.includes(op.operation)) {
|
|
493
|
-
errors.push({
|
|
494
|
-
code: "VALIDATION_ERROR",
|
|
495
|
-
operationIndex,
|
|
496
|
-
message: `Collection kind only supports "add" or "revise" operation, got "${op.operation}"`
|
|
497
|
-
});
|
|
498
|
-
return errors;
|
|
499
|
-
}
|
|
500
|
-
if (op.operation === "add") {
|
|
501
|
-
if (!op.type || !collectionTypes.includes(op.type)) {
|
|
502
|
-
errors.push({
|
|
503
|
-
code: "VALIDATION_ERROR",
|
|
504
|
-
operationIndex,
|
|
505
|
-
message: `Collection "type" must be one of: pair, triple, set, list. Got: "${op.type ?? ""}"`
|
|
506
|
-
});
|
|
507
|
-
}
|
|
508
|
-
if (!op.members || !Array.isArray(op.members)) {
|
|
509
|
-
errors.push({
|
|
510
|
-
code: "VALIDATION_ERROR",
|
|
511
|
-
operationIndex,
|
|
512
|
-
message: 'Collection requires a "members" array'
|
|
513
|
-
});
|
|
514
|
-
} else {
|
|
515
|
-
for (let i = 0; i < op.members.length; i++) {
|
|
516
|
-
if (typeof op.members[i] !== "string") {
|
|
517
|
-
errors.push({
|
|
518
|
-
code: "VALIDATION_ERROR",
|
|
519
|
-
operationIndex,
|
|
520
|
-
message: `Collection member at index ${i} must be a string`
|
|
521
|
-
});
|
|
522
|
-
}
|
|
523
|
-
}
|
|
524
|
-
}
|
|
525
|
-
if (op.type && op.members) {
|
|
526
|
-
const arityError = collectionArityError(
|
|
527
|
-
op.type,
|
|
528
|
-
op.members
|
|
529
|
-
);
|
|
530
|
-
if (arityError) {
|
|
531
|
-
errors.push({
|
|
532
|
-
code: "VALIDATION_ERROR",
|
|
533
|
-
operationIndex,
|
|
534
|
-
message: arityError
|
|
535
|
-
});
|
|
536
|
-
}
|
|
537
|
-
}
|
|
538
|
-
}
|
|
539
|
-
return errors;
|
|
540
|
-
}
|
|
388
|
+
// ../rules/src/preflight-commit.ts
|
|
541
389
|
function preflightCommitDiagnostics(operations, options) {
|
|
542
390
|
const errors = [];
|
|
543
391
|
tokenHygiene(operations, errors);
|
|
544
392
|
illegalOpSequences(operations, errors);
|
|
545
393
|
return errors;
|
|
546
394
|
}
|
|
547
|
-
function collectionArityError(tag, members) {
|
|
548
|
-
switch (tag) {
|
|
549
|
-
case "pair":
|
|
550
|
-
return members.length !== 2 ? `Pair requires exactly 2 members, got ${members.length}` : null;
|
|
551
|
-
case "triple":
|
|
552
|
-
return members.length !== 3 ? `Triple requires exactly 3 members, got ${members.length}` : null;
|
|
553
|
-
case "set":
|
|
554
|
-
case "list":
|
|
555
|
-
return members.length < 1 ? `${tag === "set" ? "Set" : "List"} requires at least 1 member, got 0` : null;
|
|
556
|
-
}
|
|
557
|
-
}
|
|
558
395
|
function getOpName(op) {
|
|
559
396
|
return op.name;
|
|
560
397
|
}
|
|
@@ -727,59 +564,239 @@ function illegalOpSequences(operations, errors, checkAddAdd) {
|
|
|
727
564
|
}
|
|
728
565
|
}
|
|
729
566
|
|
|
730
|
-
// ../rules/src/
|
|
731
|
-
var
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
"trpc",
|
|
742
|
-
"version"
|
|
743
|
-
];
|
|
744
|
-
var RESERVED_ORG_NAMES = [
|
|
745
|
-
...RESERVED_RAW_CONTENT_TOP_LEVEL_NAMES,
|
|
746
|
-
"admin",
|
|
747
|
-
"billing",
|
|
748
|
-
"blog",
|
|
749
|
-
"docs",
|
|
750
|
-
"help",
|
|
751
|
-
"login",
|
|
752
|
-
"public",
|
|
753
|
-
"settings",
|
|
754
|
-
"signup",
|
|
755
|
-
"status",
|
|
756
|
-
"support",
|
|
757
|
-
"system",
|
|
758
|
-
"warmhub",
|
|
759
|
-
"www"
|
|
760
|
-
];
|
|
761
|
-
new Set(RESERVED_ORG_NAMES);
|
|
762
|
-
|
|
763
|
-
// ../rules/src/shape-field-key.ts
|
|
764
|
-
function foldFieldName(name) {
|
|
765
|
-
const stripped = name.endsWith("?") ? name.slice(0, -1) : name;
|
|
766
|
-
return stripped.toLowerCase();
|
|
567
|
+
// ../rules/src/preflight-operation.ts
|
|
568
|
+
var collectionTypes = ["pair", "triple", "set", "list"];
|
|
569
|
+
var collectionOps = ["add", "revise"];
|
|
570
|
+
function preflightOpDiagnostics(op, operationIndex) {
|
|
571
|
+
const errors = [];
|
|
572
|
+
errors.push(...builtinShapeGuard(op, operationIndex));
|
|
573
|
+
errors.push(...contentNameGuard(op, operationIndex));
|
|
574
|
+
errors.push(...plusSignGuard(op, operationIndex));
|
|
575
|
+
errors.push(...validateCollectionAbouts(op, operationIndex));
|
|
576
|
+
errors.push(...validateCollectionOps(op, operationIndex));
|
|
577
|
+
return errors;
|
|
767
578
|
}
|
|
768
|
-
function
|
|
769
|
-
|
|
579
|
+
function builtinShapeGuard(op, operationIndex) {
|
|
580
|
+
const errors = [];
|
|
581
|
+
const name = op.name;
|
|
582
|
+
if (op.kind === "shape" && name && isBuiltinShape(name)) {
|
|
583
|
+
errors.push({
|
|
584
|
+
code: "RESERVED_NAME",
|
|
585
|
+
operationIndex,
|
|
586
|
+
message: `Shape "${name}" is a built-in shape and cannot be ${op.operation === "add" ? "created" : "revised"} manually`
|
|
587
|
+
});
|
|
588
|
+
}
|
|
589
|
+
if (op.kind === "thing" && name) {
|
|
590
|
+
const local = splitLocalPath(name);
|
|
591
|
+
if (local && isBuiltinCollectionShape(local.shapePrefix)) {
|
|
592
|
+
const message = op.operation === "add" ? `Cannot add kind="thing" under built-in shape "${local.shapePrefix}". Use kind="collection" or the about sugar instead.` : `Cannot revise kind="thing" under built-in shape "${local.shapePrefix}". Collections are immutable.`;
|
|
593
|
+
errors.push({
|
|
594
|
+
code: "VALIDATION_ERROR",
|
|
595
|
+
operationIndex,
|
|
596
|
+
message
|
|
597
|
+
});
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
return errors;
|
|
770
601
|
}
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
602
|
+
function contentNameGuard(op, operationIndex) {
|
|
603
|
+
if (op.kind !== "thing" || !op.name) return [];
|
|
604
|
+
const local = splitLocalPath(op.name);
|
|
605
|
+
if (!local || !isContentShape(local.shapePrefix)) return [];
|
|
606
|
+
const bareName = local.bareName;
|
|
607
|
+
if (isSynthesizedContentName(bareName)) {
|
|
608
|
+
return [
|
|
609
|
+
{
|
|
610
|
+
code: "READ_ONLY_BUILTIN_CONTENT",
|
|
611
|
+
operationIndex,
|
|
612
|
+
message: `${local.shapePrefix}/${bareName} is synthesized; writes are not allowed`
|
|
613
|
+
}
|
|
614
|
+
];
|
|
615
|
+
}
|
|
616
|
+
if (!isStoredContentName(bareName)) {
|
|
617
|
+
return [
|
|
618
|
+
{
|
|
619
|
+
code: "UNKNOWN_CONTENT_NAME",
|
|
620
|
+
operationIndex,
|
|
621
|
+
message: `${local.shapePrefix}/${bareName} is not a recognized well-known content name`
|
|
622
|
+
}
|
|
623
|
+
];
|
|
624
|
+
}
|
|
625
|
+
return [];
|
|
626
|
+
}
|
|
627
|
+
function plusSignGuard(op, operationIndex) {
|
|
628
|
+
const errors = [];
|
|
629
|
+
const name = op.name;
|
|
630
|
+
if (op.kind === "collection") return errors;
|
|
631
|
+
if (name?.includes("+")) {
|
|
632
|
+
const local = splitLocalPath(name);
|
|
633
|
+
if (local && isBuiltinCollectionShape(local.shapePrefix)) return errors;
|
|
634
|
+
errors.push({
|
|
635
|
+
code: "VALIDATION_ERROR",
|
|
636
|
+
operationIndex,
|
|
637
|
+
message: `Name "${name}" contains reserved character "+". The "+" character is reserved for collection names.`
|
|
638
|
+
});
|
|
639
|
+
}
|
|
640
|
+
return errors;
|
|
641
|
+
}
|
|
642
|
+
function validateCollectionAbouts(op, operationIndex) {
|
|
643
|
+
const errors = [];
|
|
644
|
+
if (!op.about || typeof op.about === "string") return errors;
|
|
645
|
+
if (!isCollectionAbout(op.about)) {
|
|
646
|
+
const keys = typeof op.about === "object" && op.about !== null ? Object.keys(op.about) : [];
|
|
647
|
+
errors.push({
|
|
648
|
+
code: "VALIDATION_ERROR",
|
|
649
|
+
operationIndex,
|
|
650
|
+
message: `Structured about must have exactly one key: pair, triple, set, or list. Got: ${keys.join(", ") || typeof op.about}`
|
|
651
|
+
});
|
|
652
|
+
return errors;
|
|
653
|
+
}
|
|
654
|
+
const tag = collectionAboutType(op.about);
|
|
655
|
+
const members = collectionAboutMembers(op.about);
|
|
656
|
+
for (let i = 0; i < members.length; i++) {
|
|
657
|
+
if (typeof members[i] !== "string") {
|
|
658
|
+
errors.push({
|
|
659
|
+
code: "VALIDATION_ERROR",
|
|
660
|
+
operationIndex,
|
|
661
|
+
message: `Collection member at index ${i} must be a string, got ${typeof members[i]}`
|
|
662
|
+
});
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
const arityError = collectionArityError(tag, members);
|
|
666
|
+
if (arityError) {
|
|
667
|
+
errors.push({
|
|
668
|
+
code: "VALIDATION_ERROR",
|
|
669
|
+
operationIndex,
|
|
670
|
+
message: arityError
|
|
671
|
+
});
|
|
672
|
+
}
|
|
673
|
+
return errors;
|
|
674
|
+
}
|
|
675
|
+
function validateCollectionOps(op, operationIndex) {
|
|
676
|
+
const errors = [];
|
|
677
|
+
if (op.kind !== "collection") return errors;
|
|
678
|
+
if (!collectionOps.includes(op.operation)) {
|
|
679
|
+
errors.push({
|
|
680
|
+
code: "VALIDATION_ERROR",
|
|
681
|
+
operationIndex,
|
|
682
|
+
message: `Collection kind only supports "add" or "revise" operation, got "${op.operation}"`
|
|
683
|
+
});
|
|
684
|
+
return errors;
|
|
685
|
+
}
|
|
686
|
+
if (op.operation === "add") {
|
|
687
|
+
if (!op.type || !collectionTypes.includes(op.type)) {
|
|
688
|
+
errors.push({
|
|
689
|
+
code: "VALIDATION_ERROR",
|
|
690
|
+
operationIndex,
|
|
691
|
+
message: `Collection "type" must be one of: pair, triple, set, list. Got: "${op.type ?? ""}"`
|
|
692
|
+
});
|
|
693
|
+
}
|
|
694
|
+
if (!op.members || !Array.isArray(op.members)) {
|
|
695
|
+
errors.push({
|
|
696
|
+
code: "VALIDATION_ERROR",
|
|
697
|
+
operationIndex,
|
|
698
|
+
message: 'Collection requires a "members" array'
|
|
699
|
+
});
|
|
700
|
+
} else {
|
|
701
|
+
for (let i = 0; i < op.members.length; i++) {
|
|
702
|
+
if (typeof op.members[i] !== "string") {
|
|
703
|
+
errors.push({
|
|
704
|
+
code: "VALIDATION_ERROR",
|
|
705
|
+
operationIndex,
|
|
706
|
+
message: `Collection member at index ${i} must be a string`
|
|
707
|
+
});
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
if (op.type && op.members) {
|
|
712
|
+
const arityError = collectionArityError(
|
|
713
|
+
op.type,
|
|
714
|
+
op.members
|
|
715
|
+
);
|
|
716
|
+
if (arityError) {
|
|
717
|
+
errors.push({
|
|
718
|
+
code: "VALIDATION_ERROR",
|
|
719
|
+
operationIndex,
|
|
720
|
+
message: arityError
|
|
721
|
+
});
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
return errors;
|
|
726
|
+
}
|
|
727
|
+
function collectionArityError(tag, members) {
|
|
728
|
+
switch (tag) {
|
|
729
|
+
case "pair":
|
|
730
|
+
return members.length !== 2 ? `Pair requires exactly 2 members, got ${members.length}` : null;
|
|
731
|
+
case "triple":
|
|
732
|
+
return members.length !== 3 ? `Triple requires exactly 3 members, got ${members.length}` : null;
|
|
733
|
+
case "set":
|
|
734
|
+
case "list":
|
|
735
|
+
return members.length < 1 ? `${tag === "set" ? "Set" : "List"} requires at least 1 member, got 0` : null;
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
// ../rules/src/preflight.ts
|
|
740
|
+
function preflightOpDiagnostics2(op, operationIndex) {
|
|
741
|
+
return preflightOpDiagnostics(op, operationIndex);
|
|
742
|
+
}
|
|
743
|
+
function preflightCommitDiagnostics2(operations, options) {
|
|
744
|
+
return preflightCommitDiagnostics(operations);
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
// ../rules/src/reserved-orgs.ts
|
|
748
|
+
var RESERVED_RAW_CONTENT_TOP_LEVEL_NAMES = [
|
|
749
|
+
"_app",
|
|
750
|
+
"_static",
|
|
751
|
+
"api",
|
|
752
|
+
"health",
|
|
753
|
+
"healthz",
|
|
754
|
+
"mcp",
|
|
755
|
+
"readyz",
|
|
756
|
+
"robots.txt",
|
|
757
|
+
"sse",
|
|
758
|
+
"trpc",
|
|
759
|
+
"version"
|
|
760
|
+
];
|
|
761
|
+
var RESERVED_ORG_NAMES = [
|
|
762
|
+
...RESERVED_RAW_CONTENT_TOP_LEVEL_NAMES,
|
|
763
|
+
"admin",
|
|
764
|
+
"billing",
|
|
765
|
+
"blog",
|
|
766
|
+
"docs",
|
|
767
|
+
"help",
|
|
768
|
+
"login",
|
|
769
|
+
"public",
|
|
770
|
+
"settings",
|
|
771
|
+
"signup",
|
|
772
|
+
"status",
|
|
773
|
+
"support",
|
|
774
|
+
"system",
|
|
775
|
+
"warmhub",
|
|
776
|
+
"www"
|
|
777
|
+
];
|
|
778
|
+
new Set(RESERVED_ORG_NAMES);
|
|
779
|
+
|
|
780
|
+
// ../rules/src/shape-field-key.ts
|
|
781
|
+
function foldFieldName(name) {
|
|
782
|
+
const stripped = name.endsWith("?") ? name.slice(0, -1) : name;
|
|
783
|
+
return stripped.toLowerCase();
|
|
784
|
+
}
|
|
785
|
+
function foldFieldPath(path) {
|
|
786
|
+
return path.split(".").map((segment) => foldFieldName(segment)).join(".");
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
// ../rules/src/shape-types.ts
|
|
790
|
+
var BASE_PRIMITIVE_TYPES = [
|
|
791
|
+
"number",
|
|
792
|
+
"string",
|
|
793
|
+
"boolean",
|
|
794
|
+
"wref",
|
|
795
|
+
"array"
|
|
796
|
+
];
|
|
797
|
+
var BASE_PRIMITIVE_TYPE_SET = new Set(BASE_PRIMITIVE_TYPES);
|
|
798
|
+
var VALID_PRIMITIVE_TYPES = /* @__PURE__ */ new Set([
|
|
799
|
+
"number",
|
|
783
800
|
"string",
|
|
784
801
|
"boolean",
|
|
785
802
|
"wref",
|
|
@@ -7129,7 +7146,7 @@ var RE2JS = class RE2JS2 {
|
|
|
7129
7146
|
}
|
|
7130
7147
|
};
|
|
7131
7148
|
|
|
7132
|
-
// ../rules/src/shape-validation.ts
|
|
7149
|
+
// ../rules/src/shape-validation-types.ts
|
|
7133
7150
|
var MAX_CONTENT_FIELD_BYTES = 64 * 1024;
|
|
7134
7151
|
var MAX_INDEXABLE_SCALAR_FIELDS_PER_SHAPE = 256;
|
|
7135
7152
|
var UNSUPPORTED_REGEX_SYNTAX_REASON = "uses unsupported regex syntax";
|
|
@@ -7206,162 +7223,301 @@ function normalizeOptionalTypeSpec(spec) {
|
|
|
7206
7223
|
}
|
|
7207
7224
|
return { spec, optionalByType: false };
|
|
7208
7225
|
}
|
|
7209
|
-
|
|
7210
|
-
|
|
7211
|
-
|
|
7212
|
-
|
|
7213
|
-
}
|
|
7214
|
-
function isAllowedLegacyUnsafeFieldPath(context, path) {
|
|
7215
|
-
return context.allowUnsafeFieldNamePaths.has(path);
|
|
7226
|
+
|
|
7227
|
+
// ../rules/src/shape-validation-runtime.ts
|
|
7228
|
+
function validateAgainstShape(data, shapeFields) {
|
|
7229
|
+
return compileShapeValidator(shapeFields)(data);
|
|
7216
7230
|
}
|
|
7217
|
-
function
|
|
7218
|
-
const
|
|
7219
|
-
|
|
7220
|
-
|
|
7221
|
-
`Invalid typed field object at "${path}": "type" must be a string`
|
|
7222
|
-
);
|
|
7223
|
-
return;
|
|
7231
|
+
function declaredFieldNames(shapeFields) {
|
|
7232
|
+
const declared = /* @__PURE__ */ new Set();
|
|
7233
|
+
for (const key of Object.keys(shapeFields)) {
|
|
7234
|
+
declared.add(key.endsWith("?") ? key.slice(0, -1) : key);
|
|
7224
7235
|
}
|
|
7225
|
-
|
|
7226
|
-
|
|
7227
|
-
|
|
7228
|
-
|
|
7229
|
-
|
|
7236
|
+
return declared;
|
|
7237
|
+
}
|
|
7238
|
+
function buildUndeclaredFieldsWarningFromDeclaredSet(data, declared) {
|
|
7239
|
+
const undeclaredFields = [];
|
|
7240
|
+
let totalUndeclared = 0;
|
|
7241
|
+
for (const key of Object.keys(data)) {
|
|
7242
|
+
if (declared.has(key)) continue;
|
|
7243
|
+
totalUndeclared++;
|
|
7244
|
+
if (undeclaredFields.length < MAX_UNDECLARED_FIELDS_REPORTED) {
|
|
7245
|
+
undeclaredFields.push(key);
|
|
7246
|
+
}
|
|
7230
7247
|
}
|
|
7231
|
-
if (
|
|
7232
|
-
|
|
7233
|
-
|
|
7234
|
-
|
|
7248
|
+
if (totalUndeclared === 0) return void 0;
|
|
7249
|
+
if (totalUndeclared > MAX_UNDECLARED_FIELDS_REPORTED) {
|
|
7250
|
+
return {
|
|
7251
|
+
undeclaredFields,
|
|
7252
|
+
undeclaredFieldsTruncated: true,
|
|
7253
|
+
totalUndeclared
|
|
7254
|
+
};
|
|
7235
7255
|
}
|
|
7236
|
-
|
|
7237
|
-
|
|
7238
|
-
|
|
7239
|
-
|
|
7240
|
-
|
|
7241
|
-
|
|
7242
|
-
|
|
7243
|
-
|
|
7244
|
-
|
|
7245
|
-
}
|
|
7246
|
-
}
|
|
7247
|
-
|
|
7248
|
-
|
|
7249
|
-
|
|
7250
|
-
)
|
|
7251
|
-
|
|
7252
|
-
|
|
7253
|
-
|
|
7254
|
-
errors.push(
|
|
7255
|
-
`Constraint "${safeKey}" at "${path}" is not valid for type "wref"`
|
|
7256
|
-
);
|
|
7257
|
-
}
|
|
7258
|
-
} else if (baseType === "array") {
|
|
7259
|
-
if (!ARRAY_CONSTRAINT_KEYS.has(key)) {
|
|
7260
|
-
errors.push(
|
|
7261
|
-
`Constraint "${safeKey}" at "${path}" is not valid for type "array"`
|
|
7262
|
-
);
|
|
7256
|
+
return { undeclaredFields };
|
|
7257
|
+
}
|
|
7258
|
+
var MAX_UNDECLARED_FIELDS_REPORTED = 500;
|
|
7259
|
+
function compileShapeValidator(shapeFields) {
|
|
7260
|
+
const compiledFields = compileObjectFields(shapeFields);
|
|
7261
|
+
const declared = declaredFieldNames(shapeFields);
|
|
7262
|
+
return (data) => {
|
|
7263
|
+
const errors = [];
|
|
7264
|
+
if (typeof data !== "object" || data === null || Array.isArray(data)) {
|
|
7265
|
+
return { valid: false, errors: ["Data must be a plain object"] };
|
|
7266
|
+
}
|
|
7267
|
+
for (const field of compiledFields) {
|
|
7268
|
+
const value = data[field.fieldName];
|
|
7269
|
+
if (value === void 0 || value === null) {
|
|
7270
|
+
if (!field.isOptional) {
|
|
7271
|
+
errors.push(`Missing required field: "${field.displayName}"`);
|
|
7272
|
+
}
|
|
7273
|
+
continue;
|
|
7263
7274
|
}
|
|
7264
|
-
|
|
7265
|
-
errors.push(
|
|
7266
|
-
`Constraint "${safeKey}" at "${path}" is not valid for type "${baseType}"`
|
|
7267
|
-
);
|
|
7275
|
+
field.validate(field.displayName, value, errors);
|
|
7268
7276
|
}
|
|
7277
|
+
validatePersistedStringFieldLimits(data, shapeFields, errors);
|
|
7278
|
+
const warnings = buildUndeclaredFieldsWarningFromDeclaredSet(data, declared);
|
|
7279
|
+
if (errors.length > 0) {
|
|
7280
|
+
return warnings ? { valid: false, errors, warnings } : { valid: false, errors };
|
|
7281
|
+
}
|
|
7282
|
+
return warnings ? { valid: true, warnings } : { valid: true };
|
|
7283
|
+
};
|
|
7284
|
+
}
|
|
7285
|
+
function validatePersistedStringFieldLimits(value, typeDef, errors, path) {
|
|
7286
|
+
const normalized = normalizeOptionalTypeSpec(typeDef).spec;
|
|
7287
|
+
if (typeof value === "string") {
|
|
7288
|
+
if (isDeclaredStringType(normalized) || isDeclaredWrefType(normalized))
|
|
7289
|
+
return;
|
|
7290
|
+
if (normalized !== void 0) return;
|
|
7291
|
+
assertContentFieldWithinLimit(path ?? "<value>", value, errors);
|
|
7292
|
+
return;
|
|
7269
7293
|
}
|
|
7270
|
-
if (
|
|
7271
|
-
|
|
7272
|
-
|
|
7273
|
-
)
|
|
7294
|
+
if (Array.isArray(value)) {
|
|
7295
|
+
const elementType = arrayElementType(normalized);
|
|
7296
|
+
if (normalized !== void 0 && elementType === void 0) return;
|
|
7297
|
+
for (let i = 0; i < value.length; i++) {
|
|
7298
|
+
validatePersistedStringFieldLimits(
|
|
7299
|
+
value[i],
|
|
7300
|
+
elementType,
|
|
7301
|
+
errors,
|
|
7302
|
+
`${path ?? "<value>"}[${i}]`
|
|
7303
|
+
);
|
|
7304
|
+
}
|
|
7305
|
+
return;
|
|
7274
7306
|
}
|
|
7275
|
-
if (
|
|
7276
|
-
|
|
7277
|
-
|
|
7307
|
+
if (!isPlainObject(value)) return;
|
|
7308
|
+
const fields = nestedObjectFields(normalized);
|
|
7309
|
+
if (normalized !== void 0 && fields === void 0) return;
|
|
7310
|
+
for (const [key, nestedValue] of Object.entries(value)) {
|
|
7311
|
+
validatePersistedStringFieldLimits(
|
|
7312
|
+
nestedValue,
|
|
7313
|
+
fields?.get(key),
|
|
7314
|
+
errors,
|
|
7315
|
+
path ? joinFieldPath(path, key) : escapeFieldNameForDisplay(key)
|
|
7278
7316
|
);
|
|
7279
7317
|
}
|
|
7280
|
-
|
|
7281
|
-
|
|
7318
|
+
}
|
|
7319
|
+
function isDeclaredStringType(typeDef) {
|
|
7320
|
+
if (typeDef === "string") return true;
|
|
7321
|
+
return isTypeSpecObject(typeDef) && typeDef.type === "string";
|
|
7322
|
+
}
|
|
7323
|
+
function isDeclaredWrefType(typeDef) {
|
|
7324
|
+
if (typeDef === "wref") return true;
|
|
7325
|
+
return isTypeSpecObject(typeDef) && typeDef.type === "wref";
|
|
7326
|
+
}
|
|
7327
|
+
function arrayElementType(typeDef) {
|
|
7328
|
+
if (Array.isArray(typeDef)) return typeDef[0];
|
|
7329
|
+
if (isTypeSpecObject(typeDef) && typeDef.type === "array")
|
|
7330
|
+
return typeDef.items;
|
|
7331
|
+
return void 0;
|
|
7332
|
+
}
|
|
7333
|
+
function nestedObjectFields(typeDef) {
|
|
7334
|
+
if (!isPlainObject(typeDef) || isTypeSpecObject(typeDef)) return void 0;
|
|
7335
|
+
const fields = /* @__PURE__ */ new Map();
|
|
7336
|
+
for (const [key, value] of Object.entries(typeDef)) {
|
|
7337
|
+
fields.set(key.endsWith("?") ? key.slice(0, -1) : key, value);
|
|
7282
7338
|
}
|
|
7283
|
-
|
|
7284
|
-
|
|
7285
|
-
|
|
7286
|
-
|
|
7339
|
+
return fields;
|
|
7340
|
+
}
|
|
7341
|
+
function compileObjectFields(shapeFields) {
|
|
7342
|
+
return Object.entries(shapeFields).map(([fieldSpec, rawTypeDef]) => {
|
|
7343
|
+
const isOptionalByKey = fieldSpec.endsWith("?");
|
|
7344
|
+
const fieldName = isOptionalByKey ? fieldSpec.slice(0, -1) : fieldSpec;
|
|
7345
|
+
const normalized = normalizeOptionalTypeSpec(rawTypeDef);
|
|
7346
|
+
return {
|
|
7347
|
+
fieldName,
|
|
7348
|
+
displayName: escapeFieldNameForDisplay(fieldName),
|
|
7349
|
+
isOptional: isOptionalByKey || normalized.optionalByType,
|
|
7350
|
+
validate: compileTypeValidator(normalized.spec)
|
|
7351
|
+
};
|
|
7352
|
+
});
|
|
7353
|
+
}
|
|
7354
|
+
function compileTypeValidator(typeDef) {
|
|
7355
|
+
if (typeof typeDef === "string") {
|
|
7356
|
+
return (path, value, errors) => validatePrimitiveField(path, value, typeDef, errors);
|
|
7287
7357
|
}
|
|
7288
|
-
if (
|
|
7289
|
-
|
|
7290
|
-
|
|
7291
|
-
|
|
7358
|
+
if (Array.isArray(typeDef)) {
|
|
7359
|
+
const elementValidator = typeDef.length > 0 ? compileTypeValidator(normalizeOptionalTypeSpec(typeDef[0]).spec) : void 0;
|
|
7360
|
+
return (path, value, errors) => {
|
|
7361
|
+
if (!Array.isArray(value)) {
|
|
7362
|
+
errors.push(`Field "${path}" expected array, got ${typeof value}`);
|
|
7363
|
+
} else if (elementValidator) {
|
|
7364
|
+
for (let i = 0; i < value.length; i++) {
|
|
7365
|
+
elementValidator(`${path}[${i}]`, value[i], errors);
|
|
7366
|
+
}
|
|
7367
|
+
}
|
|
7368
|
+
};
|
|
7292
7369
|
}
|
|
7293
|
-
if (
|
|
7294
|
-
|
|
7370
|
+
if (isTypeSpecObject(typeDef)) {
|
|
7371
|
+
const baseType = typeDef.type.endsWith("?") ? typeDef.type.slice(0, -1) : typeDef.type;
|
|
7372
|
+
if (baseType === "array") {
|
|
7373
|
+
const elementValidator = "items" in typeDef && typeDef.items != null ? compileTypeValidator(normalizeOptionalTypeSpec(typeDef.items).spec) : void 0;
|
|
7374
|
+
return (path, value, errors) => {
|
|
7375
|
+
if (!Array.isArray(value)) {
|
|
7376
|
+
errors.push(`Field "${path}" expected array, got ${typeof value}`);
|
|
7377
|
+
return;
|
|
7378
|
+
}
|
|
7379
|
+
if (typeof typeDef.minItems === "number" && value.length < typeDef.minItems) {
|
|
7380
|
+
errors.push(
|
|
7381
|
+
`Field "${path}" has ${value.length} items, minimum is ${typeDef.minItems}`
|
|
7382
|
+
);
|
|
7383
|
+
}
|
|
7384
|
+
if (typeof typeDef.maxItems === "number" && value.length > typeDef.maxItems) {
|
|
7385
|
+
errors.push(
|
|
7386
|
+
`Field "${path}" has ${value.length} items, maximum is ${typeDef.maxItems}`
|
|
7387
|
+
);
|
|
7388
|
+
}
|
|
7389
|
+
if (elementValidator) {
|
|
7390
|
+
for (let i = 0; i < value.length; i++) {
|
|
7391
|
+
elementValidator(`${path}[${i}]`, value[i], errors);
|
|
7392
|
+
}
|
|
7393
|
+
}
|
|
7394
|
+
};
|
|
7395
|
+
}
|
|
7396
|
+
const validateConstraintsCompiled = compileConstraintsValidator(typeDef);
|
|
7397
|
+
return (path, value, errors) => {
|
|
7398
|
+
validatePrimitiveField(path, value, typeDef.type, errors);
|
|
7399
|
+
validateConstraintsCompiled(path, value, errors);
|
|
7400
|
+
};
|
|
7295
7401
|
}
|
|
7296
|
-
if (
|
|
7297
|
-
|
|
7298
|
-
|
|
7299
|
-
|
|
7300
|
-
|
|
7301
|
-
|
|
7302
|
-
errors.push(`"pattern" at "${path}" ${unsupportedReason}`);
|
|
7402
|
+
if (isPlainObject(typeDef)) {
|
|
7403
|
+
const compiledFields = compileObjectFields(typeDef);
|
|
7404
|
+
return (path, value, errors) => {
|
|
7405
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
7406
|
+
errors.push(`Field "${path}" expected object, got ${typeof value}`);
|
|
7407
|
+
return;
|
|
7303
7408
|
}
|
|
7304
|
-
|
|
7409
|
+
const objValue = value;
|
|
7410
|
+
for (const field of compiledFields) {
|
|
7411
|
+
const subValue = objValue[field.fieldName];
|
|
7412
|
+
const subPath = joinFieldPath(path, field.fieldName);
|
|
7413
|
+
if (subValue === void 0 || subValue === null) {
|
|
7414
|
+
if (!field.isOptional) {
|
|
7415
|
+
errors.push(`Missing required field: "${subPath}"`);
|
|
7416
|
+
}
|
|
7417
|
+
continue;
|
|
7418
|
+
}
|
|
7419
|
+
field.validate(subPath, subValue, errors);
|
|
7420
|
+
}
|
|
7421
|
+
};
|
|
7305
7422
|
}
|
|
7306
|
-
|
|
7307
|
-
|
|
7308
|
-
|
|
7309
|
-
|
|
7310
|
-
|
|
7311
|
-
|
|
7312
|
-
|
|
7423
|
+
return () => {
|
|
7424
|
+
};
|
|
7425
|
+
}
|
|
7426
|
+
function compileConstraintsValidator(spec) {
|
|
7427
|
+
const pattern = typeof spec.pattern === "string" ? spec.pattern : void 0;
|
|
7428
|
+
const patternConstraint = pattern === void 0 ? void 0 : compilePatternConstraint(pattern);
|
|
7429
|
+
return (path, value, errors) => {
|
|
7430
|
+
if (typeof value === "string") {
|
|
7431
|
+
if (typeof spec.minLength === "number" && value.length < spec.minLength) {
|
|
7432
|
+
errors.push(
|
|
7433
|
+
`Field "${path}" length ${value.length} is below minimum ${spec.minLength}`
|
|
7434
|
+
);
|
|
7435
|
+
}
|
|
7436
|
+
if (typeof spec.maxLength === "number" && value.length > spec.maxLength) {
|
|
7437
|
+
errors.push(
|
|
7438
|
+
`Field "${path}" length ${value.length} exceeds maximum ${spec.maxLength}`
|
|
7439
|
+
);
|
|
7440
|
+
}
|
|
7441
|
+
if (pattern !== void 0) {
|
|
7442
|
+
const safePattern = escapeFieldNameForDisplay(pattern);
|
|
7443
|
+
if (patternConstraint?.kind === "unsupported") {
|
|
7313
7444
|
errors.push(
|
|
7314
|
-
`
|
|
7445
|
+
`Field "${path}" has unsupported pattern "${safePattern}"`
|
|
7315
7446
|
);
|
|
7316
|
-
|
|
7447
|
+
} else if (patternConstraint?.kind === "invalid") {
|
|
7448
|
+
errors.push(
|
|
7449
|
+
`Field "${path}" has invalid pattern "${safePattern}" \u2014 skipping regex check`
|
|
7450
|
+
);
|
|
7451
|
+
} else if (patternConstraint?.kind === "compiled" && !patternConstraint.regex.test(value)) {
|
|
7452
|
+
errors.push(`Field "${path}" does not match pattern "${safePattern}"`);
|
|
7317
7453
|
}
|
|
7318
7454
|
}
|
|
7455
|
+
if (Array.isArray(spec.enum) && !spec.enum.includes(value)) {
|
|
7456
|
+
errors.push(
|
|
7457
|
+
`Field "${path}" value "${escapeFieldNameForDisplay(String(value))}" is not in allowed values: ${spec.enum.map((entry) => escapeFieldNameForDisplay(String(entry))).join(", ")}`
|
|
7458
|
+
);
|
|
7459
|
+
}
|
|
7319
7460
|
}
|
|
7320
|
-
|
|
7321
|
-
|
|
7322
|
-
|
|
7323
|
-
|
|
7324
|
-
|
|
7325
|
-
|
|
7326
|
-
|
|
7327
|
-
|
|
7328
|
-
|
|
7329
|
-
|
|
7330
|
-
|
|
7331
|
-
|
|
7332
|
-
|
|
7333
|
-
|
|
7334
|
-
if (typeof spec.shape !== "string") {
|
|
7335
|
-
errors.push(`"shape" at "${path}" must be a string (shape name)`);
|
|
7336
|
-
} else if (spec.shape === "") {
|
|
7337
|
-
errors.push(`"shape" at "${path}" must not be empty`);
|
|
7461
|
+
if (typeof value === "number") {
|
|
7462
|
+
if (typeof spec.minimum === "number" && value < spec.minimum) {
|
|
7463
|
+
errors.push(
|
|
7464
|
+
`Field "${path}" value ${value} is below minimum ${spec.minimum}`
|
|
7465
|
+
);
|
|
7466
|
+
}
|
|
7467
|
+
if (typeof spec.maximum === "number" && value > spec.maximum) {
|
|
7468
|
+
errors.push(
|
|
7469
|
+
`Field "${path}" value ${value} exceeds maximum ${spec.maximum}`
|
|
7470
|
+
);
|
|
7471
|
+
}
|
|
7472
|
+
if (spec.integer === true && !Number.isInteger(value)) {
|
|
7473
|
+
errors.push(`Field "${path}" must be an integer, got ${value}`);
|
|
7474
|
+
}
|
|
7338
7475
|
}
|
|
7476
|
+
};
|
|
7477
|
+
}
|
|
7478
|
+
function compilePatternConstraint(pattern) {
|
|
7479
|
+
if (nativeRegexSyntaxError(pattern)) {
|
|
7480
|
+
return { kind: "invalid" };
|
|
7339
7481
|
}
|
|
7340
|
-
|
|
7341
|
-
|
|
7342
|
-
|
|
7343
|
-
|
|
7344
|
-
|
|
7345
|
-
|
|
7346
|
-
|
|
7347
|
-
|
|
7348
|
-
|
|
7349
|
-
|
|
7350
|
-
|
|
7351
|
-
|
|
7352
|
-
`
|
|
7353
|
-
|
|
7354
|
-
|
|
7355
|
-
|
|
7356
|
-
|
|
7357
|
-
|
|
7358
|
-
|
|
7359
|
-
errors,
|
|
7360
|
-
|
|
7361
|
-
|
|
7362
|
-
|
|
7482
|
+
const regex = compilePortablePattern(pattern);
|
|
7483
|
+
return regex ? { kind: "compiled", regex } : { kind: "unsupported" };
|
|
7484
|
+
}
|
|
7485
|
+
function validatePrimitiveField(path, value, typeDef, errors) {
|
|
7486
|
+
switch (typeDef) {
|
|
7487
|
+
case "number":
|
|
7488
|
+
if (typeof value !== "number") {
|
|
7489
|
+
errors.push(`Field "${path}" expected number, got ${typeof value}`);
|
|
7490
|
+
}
|
|
7491
|
+
break;
|
|
7492
|
+
case "string":
|
|
7493
|
+
if (typeof value !== "string") {
|
|
7494
|
+
errors.push(`Field "${path}" expected string, got ${typeof value}`);
|
|
7495
|
+
} else {
|
|
7496
|
+
assertContentFieldWithinLimit(path, value, errors);
|
|
7497
|
+
}
|
|
7498
|
+
break;
|
|
7499
|
+
case "wref":
|
|
7500
|
+
if (typeof value !== "string") {
|
|
7501
|
+
errors.push(`Field "${path}" expected string, got ${typeof value}`);
|
|
7502
|
+
}
|
|
7503
|
+
break;
|
|
7504
|
+
case "boolean":
|
|
7505
|
+
if (typeof value !== "boolean") {
|
|
7506
|
+
errors.push(`Field "${path}" expected boolean, got ${typeof value}`);
|
|
7507
|
+
}
|
|
7508
|
+
break;
|
|
7509
|
+
case "array":
|
|
7510
|
+
if (!Array.isArray(value)) {
|
|
7511
|
+
errors.push(`Field "${path}" expected array, got ${typeof value}`);
|
|
7512
|
+
}
|
|
7513
|
+
break;
|
|
7363
7514
|
}
|
|
7364
7515
|
}
|
|
7516
|
+
|
|
7517
|
+
// ../rules/src/shape-validation-type-spec.ts
|
|
7518
|
+
function isAllowedLegacyUnsafeFieldPath(context, path) {
|
|
7519
|
+
return context.allowUnsafeFieldNamePaths.has(path);
|
|
7520
|
+
}
|
|
7365
7521
|
function validateTypeSpec(path, identityPath, spec, errors, context) {
|
|
7366
7522
|
if (typeof spec === "string") {
|
|
7367
7523
|
if (!VALID_PRIMITIVE_TYPES.has(spec)) {
|
|
@@ -7452,22 +7608,180 @@ function validateTypeSpec(path, identityPath, spec, errors, context) {
|
|
|
7452
7608
|
return;
|
|
7453
7609
|
}
|
|
7454
7610
|
}
|
|
7455
|
-
for (const [key, value] of Object.entries(spec)) {
|
|
7456
|
-
const bareKey = stripOptionalMarker(key);
|
|
7457
|
-
const verdict = isUnsafeFieldName(bareKey);
|
|
7458
|
-
const fieldPath = joinFieldPath(path, key);
|
|
7459
|
-
const fieldIdentityPath = joinFieldIdentityPath(identityPath, key);
|
|
7460
|
-
if (verdict.unsafe && !isAllowedLegacyUnsafeFieldPath(context, fieldIdentityPath)) {
|
|
7461
|
-
errors.push(`Invalid field name at "${fieldPath}": ${verdict.reason}`);
|
|
7462
|
-
continue;
|
|
7463
|
-
}
|
|
7464
|
-
validateTypeSpec(fieldPath, fieldIdentityPath, value, errors, context);
|
|
7611
|
+
for (const [key, value] of Object.entries(spec)) {
|
|
7612
|
+
const bareKey = stripOptionalMarker(key);
|
|
7613
|
+
const verdict = isUnsafeFieldName(bareKey);
|
|
7614
|
+
const fieldPath = joinFieldPath(path, key);
|
|
7615
|
+
const fieldIdentityPath = joinFieldIdentityPath(identityPath, key);
|
|
7616
|
+
if (verdict.unsafe && !isAllowedLegacyUnsafeFieldPath(context, fieldIdentityPath)) {
|
|
7617
|
+
errors.push(`Invalid field name at "${fieldPath}": ${verdict.reason}`);
|
|
7618
|
+
continue;
|
|
7619
|
+
}
|
|
7620
|
+
validateTypeSpec(fieldPath, fieldIdentityPath, value, errors, context);
|
|
7621
|
+
}
|
|
7622
|
+
return;
|
|
7623
|
+
}
|
|
7624
|
+
errors.push(
|
|
7625
|
+
`Invalid type at "${path}": expected string, array, or object, got ${typeof spec}`
|
|
7626
|
+
);
|
|
7627
|
+
}
|
|
7628
|
+
function validateTypedFieldObject(path, identityPath, spec, errors, context) {
|
|
7629
|
+
const typeValue = spec.type;
|
|
7630
|
+
if (typeof typeValue !== "string") {
|
|
7631
|
+
errors.push(
|
|
7632
|
+
`Invalid typed field object at "${path}": "type" must be a string`
|
|
7633
|
+
);
|
|
7634
|
+
return;
|
|
7635
|
+
}
|
|
7636
|
+
if (!VALID_PRIMITIVE_TYPES.has(typeValue)) {
|
|
7637
|
+
errors.push(
|
|
7638
|
+
`Invalid type at "${path}.type": "${escapeFieldNameForDisplay(typeValue)}" (expected number|string|boolean|wref|array, optionally with ? suffix)`
|
|
7639
|
+
);
|
|
7640
|
+
return;
|
|
7641
|
+
}
|
|
7642
|
+
if ("description" in spec && typeof spec.description !== "string") {
|
|
7643
|
+
errors.push(
|
|
7644
|
+
`Invalid typed field object at "${path}": "description" must be a string`
|
|
7645
|
+
);
|
|
7646
|
+
}
|
|
7647
|
+
const baseType = typeValue.endsWith("?") ? typeValue.slice(0, -1) : typeValue;
|
|
7648
|
+
for (const key of Object.keys(spec)) {
|
|
7649
|
+
if (key === "type" || key === "description") continue;
|
|
7650
|
+
const safeKey = escapeFieldNameForDisplay(key);
|
|
7651
|
+
if (baseType === "string") {
|
|
7652
|
+
if (!STRING_CONSTRAINT_KEYS.has(key)) {
|
|
7653
|
+
errors.push(
|
|
7654
|
+
`Constraint "${safeKey}" at "${path}" is not valid for type "string"`
|
|
7655
|
+
);
|
|
7656
|
+
}
|
|
7657
|
+
} else if (baseType === "number") {
|
|
7658
|
+
if (!NUMBER_CONSTRAINT_KEYS.has(key)) {
|
|
7659
|
+
errors.push(
|
|
7660
|
+
`Constraint "${safeKey}" at "${path}" is not valid for type "number"`
|
|
7661
|
+
);
|
|
7662
|
+
}
|
|
7663
|
+
} else if (baseType === "wref") {
|
|
7664
|
+
if (!WREF_CONSTRAINT_KEYS.has(key)) {
|
|
7665
|
+
errors.push(
|
|
7666
|
+
`Constraint "${safeKey}" at "${path}" is not valid for type "wref"`
|
|
7667
|
+
);
|
|
7668
|
+
}
|
|
7669
|
+
} else if (baseType === "array") {
|
|
7670
|
+
if (!ARRAY_CONSTRAINT_KEYS.has(key)) {
|
|
7671
|
+
errors.push(
|
|
7672
|
+
`Constraint "${safeKey}" at "${path}" is not valid for type "array"`
|
|
7673
|
+
);
|
|
7674
|
+
}
|
|
7675
|
+
} else {
|
|
7676
|
+
errors.push(
|
|
7677
|
+
`Constraint "${safeKey}" at "${path}" is not valid for type "${baseType}"`
|
|
7678
|
+
);
|
|
7679
|
+
}
|
|
7680
|
+
}
|
|
7681
|
+
if ("minLength" in spec && (typeof spec.minLength !== "number" || !Number.isFinite(spec.minLength) || spec.minLength < 0 || !Number.isInteger(spec.minLength))) {
|
|
7682
|
+
errors.push(
|
|
7683
|
+
`"minLength" at "${path}" must be a non-negative finite integer`
|
|
7684
|
+
);
|
|
7685
|
+
}
|
|
7686
|
+
if ("maxLength" in spec && (typeof spec.maxLength !== "number" || !Number.isFinite(spec.maxLength) || spec.maxLength < 0 || !Number.isInteger(spec.maxLength))) {
|
|
7687
|
+
errors.push(
|
|
7688
|
+
`"maxLength" at "${path}" must be a non-negative finite integer`
|
|
7689
|
+
);
|
|
7690
|
+
}
|
|
7691
|
+
if ("minLength" in spec && "maxLength" in spec && typeof spec.minLength === "number" && typeof spec.maxLength === "number" && spec.minLength > spec.maxLength) {
|
|
7692
|
+
errors.push(`"minLength" at "${path}" cannot exceed "maxLength"`);
|
|
7693
|
+
}
|
|
7694
|
+
if (baseType === "string" && typeof spec.minLength === "number" && Number.isFinite(spec.minLength) && Number.isInteger(spec.minLength) && spec.minLength > MAX_CONTENT_FIELD_BYTES) {
|
|
7695
|
+
errors.push(
|
|
7696
|
+
`"minLength" at "${path}" cannot exceed ${MAX_CONTENT_FIELD_BYTES}`
|
|
7697
|
+
);
|
|
7698
|
+
}
|
|
7699
|
+
if (baseType === "string" && typeof spec.maxLength === "number" && Number.isFinite(spec.maxLength) && Number.isInteger(spec.maxLength) && spec.maxLength > MAX_CONTENT_FIELD_BYTES) {
|
|
7700
|
+
errors.push(
|
|
7701
|
+
`"maxLength" at "${path}" cannot exceed ${MAX_CONTENT_FIELD_BYTES}`
|
|
7702
|
+
);
|
|
7703
|
+
}
|
|
7704
|
+
if ("pattern" in spec && typeof spec.pattern !== "string") {
|
|
7705
|
+
errors.push(`"pattern" at "${path}" must be a string`);
|
|
7706
|
+
}
|
|
7707
|
+
if ("pattern" in spec && typeof spec.pattern === "string") {
|
|
7708
|
+
if (nativeRegexSyntaxError(spec.pattern)) {
|
|
7709
|
+
errors.push(`"pattern" at "${path}" is not a valid regular expression`);
|
|
7710
|
+
} else {
|
|
7711
|
+
const unsupportedReason = unsupportedPortablePatternReason(spec.pattern);
|
|
7712
|
+
if (unsupportedReason) {
|
|
7713
|
+
errors.push(`"pattern" at "${path}" ${unsupportedReason}`);
|
|
7714
|
+
}
|
|
7715
|
+
}
|
|
7716
|
+
}
|
|
7717
|
+
if ("enum" in spec) {
|
|
7718
|
+
if (!Array.isArray(spec.enum) || spec.enum.length === 0 || !spec.enum.every((v) => typeof v === "string")) {
|
|
7719
|
+
errors.push(`"enum" at "${path}" must be a non-empty array of strings`);
|
|
7720
|
+
} else if (baseType === "string") {
|
|
7721
|
+
for (const value of spec.enum) {
|
|
7722
|
+
const byteLength = utf8ByteLength(value);
|
|
7723
|
+
if (byteLength > MAX_CONTENT_FIELD_BYTES) {
|
|
7724
|
+
errors.push(
|
|
7725
|
+
`"enum" value at "${path}" is ${byteLength} bytes; string enum values cannot exceed ${MAX_CONTENT_FIELD_BYTES} bytes`
|
|
7726
|
+
);
|
|
7727
|
+
break;
|
|
7728
|
+
}
|
|
7729
|
+
}
|
|
7730
|
+
}
|
|
7731
|
+
}
|
|
7732
|
+
if ("minimum" in spec && (typeof spec.minimum !== "number" || !Number.isFinite(spec.minimum))) {
|
|
7733
|
+
errors.push(`"minimum" at "${path}" must be a finite number`);
|
|
7734
|
+
}
|
|
7735
|
+
if ("maximum" in spec && (typeof spec.maximum !== "number" || !Number.isFinite(spec.maximum))) {
|
|
7736
|
+
errors.push(`"maximum" at "${path}" must be a finite number`);
|
|
7737
|
+
}
|
|
7738
|
+
if ("minimum" in spec && "maximum" in spec && typeof spec.minimum === "number" && typeof spec.maximum === "number" && spec.minimum > spec.maximum) {
|
|
7739
|
+
errors.push(`"minimum" at "${path}" cannot exceed "maximum"`);
|
|
7740
|
+
}
|
|
7741
|
+
if ("integer" in spec && typeof spec.integer !== "boolean") {
|
|
7742
|
+
errors.push(`"integer" at "${path}" must be a boolean`);
|
|
7743
|
+
}
|
|
7744
|
+
if ("shape" in spec) {
|
|
7745
|
+
if (typeof spec.shape !== "string") {
|
|
7746
|
+
errors.push(`"shape" at "${path}" must be a string (shape name)`);
|
|
7747
|
+
} else if (spec.shape === "") {
|
|
7748
|
+
errors.push(`"shape" at "${path}" must not be empty`);
|
|
7749
|
+
}
|
|
7750
|
+
}
|
|
7751
|
+
if ("minItems" in spec && (typeof spec.minItems !== "number" || !Number.isFinite(spec.minItems) || spec.minItems < 0 || !Number.isInteger(spec.minItems))) {
|
|
7752
|
+
errors.push(`"minItems" at "${path}" must be a non-negative finite integer`);
|
|
7753
|
+
}
|
|
7754
|
+
if ("maxItems" in spec && (typeof spec.maxItems !== "number" || !Number.isFinite(spec.maxItems) || spec.maxItems < 0 || !Number.isInteger(spec.maxItems))) {
|
|
7755
|
+
errors.push(`"maxItems" at "${path}" must be a non-negative finite integer`);
|
|
7756
|
+
}
|
|
7757
|
+
if ("minItems" in spec && "maxItems" in spec && typeof spec.minItems === "number" && typeof spec.maxItems === "number" && spec.minItems > spec.maxItems) {
|
|
7758
|
+
errors.push(`"minItems" at "${path}" cannot exceed "maxItems"`);
|
|
7759
|
+
}
|
|
7760
|
+
if (baseType === "array") {
|
|
7761
|
+
if (!("items" in spec)) {
|
|
7762
|
+
errors.push(
|
|
7763
|
+
`Typed array at "${path}" must have an "items" key specifying the element type`
|
|
7764
|
+
);
|
|
7765
|
+
} else {
|
|
7766
|
+
validateTypeSpec(
|
|
7767
|
+
`${path}.items`,
|
|
7768
|
+
`${identityPath}.items`,
|
|
7769
|
+
spec.items,
|
|
7770
|
+
errors,
|
|
7771
|
+
context
|
|
7772
|
+
);
|
|
7465
7773
|
}
|
|
7466
|
-
return;
|
|
7467
7774
|
}
|
|
7468
|
-
|
|
7469
|
-
|
|
7470
|
-
|
|
7775
|
+
}
|
|
7776
|
+
|
|
7777
|
+
// ../rules/src/shape-validation-definition.ts
|
|
7778
|
+
function validationContext(options = {}) {
|
|
7779
|
+
return {
|
|
7780
|
+
allowUnsafeFieldNamePaths: new Set(options.allowUnsafeFieldNamePaths ?? [])
|
|
7781
|
+
};
|
|
7782
|
+
}
|
|
7783
|
+
function isAllowedLegacyUnsafeFieldPath2(context, path) {
|
|
7784
|
+
return context.allowUnsafeFieldNamePaths.has(path);
|
|
7471
7785
|
}
|
|
7472
7786
|
var ALLOWED_SHAPE_KEYS = /* @__PURE__ */ new Set(["fields", "description"]);
|
|
7473
7787
|
function collectIndexableShapeFieldPaths(fields, prefix = "") {
|
|
@@ -7502,7 +7816,7 @@ function validateFieldsRecord(fields, prefix, errors, context, identityPrefix =
|
|
|
7502
7816
|
const verdict = isUnsafeFieldName(bareKey);
|
|
7503
7817
|
const fieldPath = joinFieldPath(prefix, key);
|
|
7504
7818
|
const fieldIdentityPath = joinFieldIdentityPath(identityPrefix, key);
|
|
7505
|
-
if (verdict.unsafe && !
|
|
7819
|
+
if (verdict.unsafe && !isAllowedLegacyUnsafeFieldPath2(context, fieldIdentityPath)) {
|
|
7506
7820
|
errors.push(`Invalid field name at "${fieldPath}": ${verdict.reason}`);
|
|
7507
7821
|
continue;
|
|
7508
7822
|
}
|
|
@@ -7530,8 +7844,9 @@ function validateFieldsRecord(fields, prefix, errors, context, identityPrefix =
|
|
|
7530
7844
|
}
|
|
7531
7845
|
function collectUnsafeFieldNamePathsInTypeSpec(path, spec, paths) {
|
|
7532
7846
|
if (Array.isArray(spec)) {
|
|
7533
|
-
if (spec.length === 1)
|
|
7847
|
+
if (spec.length === 1) {
|
|
7534
7848
|
collectUnsafeFieldNamePathsInTypeSpec(`${path}[]`, spec[0], paths);
|
|
7849
|
+
}
|
|
7535
7850
|
return;
|
|
7536
7851
|
}
|
|
7537
7852
|
if (isTypeSpecObject(spec)) {
|
|
@@ -7576,360 +7891,72 @@ function collectUnsafeShapeFieldNamePaths(data) {
|
|
|
7576
7891
|
paths
|
|
7577
7892
|
);
|
|
7578
7893
|
return paths;
|
|
7579
|
-
}
|
|
7580
|
-
function validateShapeDefinition(data, options = {}) {
|
|
7581
|
-
if (typeof data !== "object" || data === null || Array.isArray(data)) {
|
|
7582
|
-
return { valid: false, errors: ["Shape definition must be a plain object"] };
|
|
7583
|
-
}
|
|
7584
|
-
const obj = data;
|
|
7585
|
-
const errors = [];
|
|
7586
|
-
for (const key of Object.keys(obj)) {
|
|
7587
|
-
if (!ALLOWED_SHAPE_KEYS.has(key)) {
|
|
7588
|
-
errors.push(
|
|
7589
|
-
`Unsupported shape definition key "${escapeFieldNameForDisplay(key)}"`
|
|
7590
|
-
);
|
|
7591
|
-
}
|
|
7592
|
-
}
|
|
7593
|
-
if (!("fields" in obj)) {
|
|
7594
|
-
return {
|
|
7595
|
-
valid: false,
|
|
7596
|
-
errors: ['Shape definition must have a "fields" property']
|
|
7597
|
-
};
|
|
7598
|
-
}
|
|
7599
|
-
const fields = obj.fields;
|
|
7600
|
-
if (typeof fields !== "object" || fields === null || Array.isArray(fields)) {
|
|
7601
|
-
return {
|
|
7602
|
-
valid: false,
|
|
7603
|
-
errors: ['"fields" must be a plain object mapping field names to types']
|
|
7604
|
-
};
|
|
7605
|
-
}
|
|
7606
|
-
const context = validationContext(options);
|
|
7607
|
-
validateFieldsRecord(
|
|
7608
|
-
fields,
|
|
7609
|
-
"fields",
|
|
7610
|
-
errors,
|
|
7611
|
-
context
|
|
7612
|
-
);
|
|
7613
|
-
const indexableFieldPaths = collectIndexableShapeFieldPaths(
|
|
7614
|
-
fields
|
|
7615
|
-
);
|
|
7616
|
-
const indexableFieldCount = indexableFieldPaths.length;
|
|
7617
|
-
const seenIndexableFieldPaths = /* @__PURE__ */ new Set();
|
|
7618
|
-
const duplicateIndexableFieldPaths = /* @__PURE__ */ new Set();
|
|
7619
|
-
for (const fieldPath of indexableFieldPaths) {
|
|
7620
|
-
const foldedPath = foldFieldPath(fieldPath);
|
|
7621
|
-
if (seenIndexableFieldPaths.has(foldedPath)) {
|
|
7622
|
-
duplicateIndexableFieldPaths.add(foldedPath);
|
|
7623
|
-
continue;
|
|
7624
|
-
}
|
|
7625
|
-
seenIndexableFieldPaths.add(foldedPath);
|
|
7626
|
-
}
|
|
7627
|
-
for (const fieldPath of duplicateIndexableFieldPaths) {
|
|
7628
|
-
errors.push(
|
|
7629
|
-
`Shape declares ambiguous indexable field path "${escapeFieldNameForDisplay(fieldPath)}"; dotted field names, optional-key suffixes, and case-insensitive folded paths must not collide with another indexable path`
|
|
7630
|
-
);
|
|
7631
|
-
}
|
|
7632
|
-
if (indexableFieldCount > MAX_INDEXABLE_SCALAR_FIELDS_PER_SHAPE) {
|
|
7633
|
-
errors.push(
|
|
7634
|
-
`Shape declares ${indexableFieldCount} indexable scalar fields; maximum is ${MAX_INDEXABLE_SCALAR_FIELDS_PER_SHAPE}`
|
|
7635
|
-
);
|
|
7636
|
-
}
|
|
7637
|
-
if ("description" in obj && typeof obj.description !== "string") {
|
|
7638
|
-
errors.push('"description" must be a string');
|
|
7639
|
-
}
|
|
7640
|
-
validatePersistedStringFieldLimits(obj, void 0, errors);
|
|
7641
|
-
if (errors.length > 0) {
|
|
7642
|
-
return { valid: false, errors };
|
|
7643
|
-
}
|
|
7644
|
-
return { valid: true };
|
|
7645
|
-
}
|
|
7646
|
-
function validateAgainstShape(data, shapeFields) {
|
|
7647
|
-
return compileShapeValidator(shapeFields)(data);
|
|
7648
|
-
}
|
|
7649
|
-
function declaredFieldNames(shapeFields) {
|
|
7650
|
-
const declared = /* @__PURE__ */ new Set();
|
|
7651
|
-
for (const key of Object.keys(shapeFields)) {
|
|
7652
|
-
declared.add(key.endsWith("?") ? key.slice(0, -1) : key);
|
|
7653
|
-
}
|
|
7654
|
-
return declared;
|
|
7655
|
-
}
|
|
7656
|
-
function buildUndeclaredFieldsWarningFromDeclaredSet(data, declared) {
|
|
7657
|
-
const undeclaredFields = [];
|
|
7658
|
-
let totalUndeclared = 0;
|
|
7659
|
-
for (const key of Object.keys(data)) {
|
|
7660
|
-
if (declared.has(key)) continue;
|
|
7661
|
-
totalUndeclared++;
|
|
7662
|
-
if (undeclaredFields.length < MAX_UNDECLARED_FIELDS_REPORTED) {
|
|
7663
|
-
undeclaredFields.push(key);
|
|
7664
|
-
}
|
|
7665
|
-
}
|
|
7666
|
-
if (totalUndeclared === 0) return void 0;
|
|
7667
|
-
if (totalUndeclared > MAX_UNDECLARED_FIELDS_REPORTED) {
|
|
7668
|
-
return {
|
|
7669
|
-
undeclaredFields,
|
|
7670
|
-
undeclaredFieldsTruncated: true,
|
|
7671
|
-
totalUndeclared
|
|
7672
|
-
};
|
|
7673
|
-
}
|
|
7674
|
-
return { undeclaredFields };
|
|
7675
|
-
}
|
|
7676
|
-
var MAX_UNDECLARED_FIELDS_REPORTED = 500;
|
|
7677
|
-
function compileShapeValidator(shapeFields) {
|
|
7678
|
-
const compiledFields = compileObjectFields(shapeFields);
|
|
7679
|
-
const declared = declaredFieldNames(shapeFields);
|
|
7680
|
-
return (data) => {
|
|
7681
|
-
const errors = [];
|
|
7682
|
-
if (typeof data !== "object" || data === null || Array.isArray(data)) {
|
|
7683
|
-
return { valid: false, errors: ["Data must be a plain object"] };
|
|
7684
|
-
}
|
|
7685
|
-
for (const field of compiledFields) {
|
|
7686
|
-
const value = data[field.fieldName];
|
|
7687
|
-
if (value === void 0 || value === null) {
|
|
7688
|
-
if (!field.isOptional) {
|
|
7689
|
-
errors.push(`Missing required field: "${field.displayName}"`);
|
|
7690
|
-
}
|
|
7691
|
-
continue;
|
|
7692
|
-
}
|
|
7693
|
-
field.validate(field.displayName, value, errors);
|
|
7694
|
-
}
|
|
7695
|
-
validatePersistedStringFieldLimits(data, shapeFields, errors);
|
|
7696
|
-
const warnings = buildUndeclaredFieldsWarningFromDeclaredSet(data, declared);
|
|
7697
|
-
if (errors.length > 0) {
|
|
7698
|
-
return warnings ? { valid: false, errors, warnings } : { valid: false, errors };
|
|
7699
|
-
}
|
|
7700
|
-
return warnings ? { valid: true, warnings } : { valid: true };
|
|
7701
|
-
};
|
|
7702
|
-
}
|
|
7703
|
-
function validatePersistedStringFieldLimits(value, typeDef, errors, path) {
|
|
7704
|
-
const normalized = normalizeOptionalTypeSpec(typeDef).spec;
|
|
7705
|
-
if (typeof value === "string") {
|
|
7706
|
-
if (isDeclaredStringType(normalized) || isDeclaredWrefType(normalized))
|
|
7707
|
-
return;
|
|
7708
|
-
if (normalized !== void 0) return;
|
|
7709
|
-
assertContentFieldWithinLimit(path ?? "<value>", value, errors);
|
|
7710
|
-
return;
|
|
7711
|
-
}
|
|
7712
|
-
if (Array.isArray(value)) {
|
|
7713
|
-
const elementType = arrayElementType(normalized);
|
|
7714
|
-
if (normalized !== void 0 && elementType === void 0) return;
|
|
7715
|
-
for (let i = 0; i < value.length; i++) {
|
|
7716
|
-
validatePersistedStringFieldLimits(
|
|
7717
|
-
value[i],
|
|
7718
|
-
elementType,
|
|
7719
|
-
errors,
|
|
7720
|
-
`${path ?? "<value>"}[${i}]`
|
|
7721
|
-
);
|
|
7722
|
-
}
|
|
7723
|
-
return;
|
|
7724
|
-
}
|
|
7725
|
-
if (!isPlainObject(value)) return;
|
|
7726
|
-
const fields = nestedObjectFields(normalized);
|
|
7727
|
-
if (normalized !== void 0 && fields === void 0) return;
|
|
7728
|
-
for (const [key, nestedValue] of Object.entries(value)) {
|
|
7729
|
-
validatePersistedStringFieldLimits(
|
|
7730
|
-
nestedValue,
|
|
7731
|
-
fields?.get(key),
|
|
7732
|
-
errors,
|
|
7733
|
-
path ? joinFieldPath(path, key) : escapeFieldNameForDisplay(key)
|
|
7734
|
-
);
|
|
7735
|
-
}
|
|
7736
|
-
}
|
|
7737
|
-
function isDeclaredStringType(typeDef) {
|
|
7738
|
-
if (typeDef === "string") return true;
|
|
7739
|
-
return isTypeSpecObject(typeDef) && typeDef.type === "string";
|
|
7740
|
-
}
|
|
7741
|
-
function isDeclaredWrefType(typeDef) {
|
|
7742
|
-
if (typeDef === "wref") return true;
|
|
7743
|
-
return isTypeSpecObject(typeDef) && typeDef.type === "wref";
|
|
7744
|
-
}
|
|
7745
|
-
function arrayElementType(typeDef) {
|
|
7746
|
-
if (Array.isArray(typeDef)) return typeDef[0];
|
|
7747
|
-
if (isTypeSpecObject(typeDef) && typeDef.type === "array")
|
|
7748
|
-
return typeDef.items;
|
|
7749
|
-
return void 0;
|
|
7750
|
-
}
|
|
7751
|
-
function nestedObjectFields(typeDef) {
|
|
7752
|
-
if (!isPlainObject(typeDef) || isTypeSpecObject(typeDef)) return void 0;
|
|
7753
|
-
const fields = /* @__PURE__ */ new Map();
|
|
7754
|
-
for (const [key, value] of Object.entries(typeDef)) {
|
|
7755
|
-
fields.set(key.endsWith("?") ? key.slice(0, -1) : key, value);
|
|
7756
|
-
}
|
|
7757
|
-
return fields;
|
|
7758
|
-
}
|
|
7759
|
-
function compileObjectFields(shapeFields) {
|
|
7760
|
-
return Object.entries(shapeFields).map(([fieldSpec, rawTypeDef]) => {
|
|
7761
|
-
const isOptionalByKey = fieldSpec.endsWith("?");
|
|
7762
|
-
const fieldName = isOptionalByKey ? fieldSpec.slice(0, -1) : fieldSpec;
|
|
7763
|
-
const normalized = normalizeOptionalTypeSpec(rawTypeDef);
|
|
7764
|
-
return {
|
|
7765
|
-
fieldName,
|
|
7766
|
-
displayName: escapeFieldNameForDisplay(fieldName),
|
|
7767
|
-
isOptional: isOptionalByKey || normalized.optionalByType,
|
|
7768
|
-
validate: compileTypeValidator(normalized.spec)
|
|
7769
|
-
};
|
|
7770
|
-
});
|
|
7771
|
-
}
|
|
7772
|
-
function compileTypeValidator(typeDef) {
|
|
7773
|
-
if (typeof typeDef === "string") {
|
|
7774
|
-
return (path, value, errors) => validatePrimitiveField(path, value, typeDef, errors);
|
|
7775
|
-
}
|
|
7776
|
-
if (Array.isArray(typeDef)) {
|
|
7777
|
-
const elementValidator = typeDef.length > 0 ? compileTypeValidator(normalizeOptionalTypeSpec(typeDef[0]).spec) : void 0;
|
|
7778
|
-
return (path, value, errors) => {
|
|
7779
|
-
if (!Array.isArray(value)) {
|
|
7780
|
-
errors.push(`Field "${path}" expected array, got ${typeof value}`);
|
|
7781
|
-
} else if (elementValidator) {
|
|
7782
|
-
for (let i = 0; i < value.length; i++) {
|
|
7783
|
-
elementValidator(`${path}[${i}]`, value[i], errors);
|
|
7784
|
-
}
|
|
7785
|
-
}
|
|
7786
|
-
};
|
|
7894
|
+
}
|
|
7895
|
+
function validateShapeDefinition(data, options = {}) {
|
|
7896
|
+
if (typeof data !== "object" || data === null || Array.isArray(data)) {
|
|
7897
|
+
return { valid: false, errors: ["Shape definition must be a plain object"] };
|
|
7787
7898
|
}
|
|
7788
|
-
|
|
7789
|
-
|
|
7790
|
-
|
|
7791
|
-
|
|
7792
|
-
|
|
7793
|
-
|
|
7794
|
-
|
|
7795
|
-
return;
|
|
7796
|
-
}
|
|
7797
|
-
if (typeof typeDef.minItems === "number" && value.length < typeDef.minItems) {
|
|
7798
|
-
errors.push(
|
|
7799
|
-
`Field "${path}" has ${value.length} items, minimum is ${typeDef.minItems}`
|
|
7800
|
-
);
|
|
7801
|
-
}
|
|
7802
|
-
if (typeof typeDef.maxItems === "number" && value.length > typeDef.maxItems) {
|
|
7803
|
-
errors.push(
|
|
7804
|
-
`Field "${path}" has ${value.length} items, maximum is ${typeDef.maxItems}`
|
|
7805
|
-
);
|
|
7806
|
-
}
|
|
7807
|
-
if (elementValidator) {
|
|
7808
|
-
for (let i = 0; i < value.length; i++) {
|
|
7809
|
-
elementValidator(`${path}[${i}]`, value[i], errors);
|
|
7810
|
-
}
|
|
7811
|
-
}
|
|
7812
|
-
};
|
|
7899
|
+
const obj = data;
|
|
7900
|
+
const errors = [];
|
|
7901
|
+
for (const key of Object.keys(obj)) {
|
|
7902
|
+
if (!ALLOWED_SHAPE_KEYS.has(key)) {
|
|
7903
|
+
errors.push(
|
|
7904
|
+
`Unsupported shape definition key "${escapeFieldNameForDisplay(key)}"`
|
|
7905
|
+
);
|
|
7813
7906
|
}
|
|
7814
|
-
|
|
7815
|
-
|
|
7816
|
-
|
|
7817
|
-
|
|
7907
|
+
}
|
|
7908
|
+
if (!("fields" in obj)) {
|
|
7909
|
+
return {
|
|
7910
|
+
valid: false,
|
|
7911
|
+
errors: ['Shape definition must have a "fields" property']
|
|
7818
7912
|
};
|
|
7819
7913
|
}
|
|
7820
|
-
|
|
7821
|
-
|
|
7822
|
-
return
|
|
7823
|
-
|
|
7824
|
-
|
|
7825
|
-
return;
|
|
7826
|
-
}
|
|
7827
|
-
const objValue = value;
|
|
7828
|
-
for (const field of compiledFields) {
|
|
7829
|
-
const subValue = objValue[field.fieldName];
|
|
7830
|
-
const subPath = joinFieldPath(path, field.fieldName);
|
|
7831
|
-
if (subValue === void 0 || subValue === null) {
|
|
7832
|
-
if (!field.isOptional) {
|
|
7833
|
-
errors.push(`Missing required field: "${subPath}"`);
|
|
7834
|
-
}
|
|
7835
|
-
continue;
|
|
7836
|
-
}
|
|
7837
|
-
field.validate(subPath, subValue, errors);
|
|
7838
|
-
}
|
|
7914
|
+
const fields = obj.fields;
|
|
7915
|
+
if (typeof fields !== "object" || fields === null || Array.isArray(fields)) {
|
|
7916
|
+
return {
|
|
7917
|
+
valid: false,
|
|
7918
|
+
errors: ['"fields" must be a plain object mapping field names to types']
|
|
7839
7919
|
};
|
|
7840
7920
|
}
|
|
7841
|
-
|
|
7842
|
-
|
|
7843
|
-
|
|
7844
|
-
|
|
7845
|
-
|
|
7846
|
-
|
|
7847
|
-
|
|
7848
|
-
|
|
7849
|
-
|
|
7850
|
-
|
|
7851
|
-
|
|
7852
|
-
|
|
7853
|
-
|
|
7854
|
-
|
|
7855
|
-
|
|
7856
|
-
|
|
7857
|
-
|
|
7858
|
-
|
|
7859
|
-
if (pattern !== void 0) {
|
|
7860
|
-
const safePattern = escapeFieldNameForDisplay(pattern);
|
|
7861
|
-
if (patternConstraint?.kind === "unsupported") {
|
|
7862
|
-
errors.push(
|
|
7863
|
-
`Field "${path}" has unsupported pattern "${safePattern}"`
|
|
7864
|
-
);
|
|
7865
|
-
} else if (patternConstraint?.kind === "invalid") {
|
|
7866
|
-
errors.push(
|
|
7867
|
-
`Field "${path}" has invalid pattern "${safePattern}" \u2014 skipping regex check`
|
|
7868
|
-
);
|
|
7869
|
-
} else if (patternConstraint?.kind === "compiled" && !patternConstraint.regex.test(value)) {
|
|
7870
|
-
errors.push(`Field "${path}" does not match pattern "${safePattern}"`);
|
|
7871
|
-
}
|
|
7872
|
-
}
|
|
7873
|
-
if (Array.isArray(spec.enum) && !spec.enum.includes(value)) {
|
|
7874
|
-
errors.push(
|
|
7875
|
-
`Field "${path}" value "${escapeFieldNameForDisplay(String(value))}" is not in allowed values: ${spec.enum.map((entry) => escapeFieldNameForDisplay(String(entry))).join(", ")}`
|
|
7876
|
-
);
|
|
7877
|
-
}
|
|
7878
|
-
}
|
|
7879
|
-
if (typeof value === "number") {
|
|
7880
|
-
if (typeof spec.minimum === "number" && value < spec.minimum) {
|
|
7881
|
-
errors.push(
|
|
7882
|
-
`Field "${path}" value ${value} is below minimum ${spec.minimum}`
|
|
7883
|
-
);
|
|
7884
|
-
}
|
|
7885
|
-
if (typeof spec.maximum === "number" && value > spec.maximum) {
|
|
7886
|
-
errors.push(
|
|
7887
|
-
`Field "${path}" value ${value} exceeds maximum ${spec.maximum}`
|
|
7888
|
-
);
|
|
7889
|
-
}
|
|
7890
|
-
if (spec.integer === true && !Number.isInteger(value)) {
|
|
7891
|
-
errors.push(`Field "${path}" must be an integer, got ${value}`);
|
|
7892
|
-
}
|
|
7921
|
+
const context = validationContext(options);
|
|
7922
|
+
validateFieldsRecord(
|
|
7923
|
+
fields,
|
|
7924
|
+
"fields",
|
|
7925
|
+
errors,
|
|
7926
|
+
context
|
|
7927
|
+
);
|
|
7928
|
+
const indexableFieldPaths = collectIndexableShapeFieldPaths(
|
|
7929
|
+
fields
|
|
7930
|
+
);
|
|
7931
|
+
const indexableFieldCount = indexableFieldPaths.length;
|
|
7932
|
+
const seenIndexableFieldPaths = /* @__PURE__ */ new Set();
|
|
7933
|
+
const duplicateIndexableFieldPaths = /* @__PURE__ */ new Set();
|
|
7934
|
+
for (const fieldPath of indexableFieldPaths) {
|
|
7935
|
+
const foldedPath = foldFieldPath(fieldPath);
|
|
7936
|
+
if (seenIndexableFieldPaths.has(foldedPath)) {
|
|
7937
|
+
duplicateIndexableFieldPaths.add(foldedPath);
|
|
7938
|
+
continue;
|
|
7893
7939
|
}
|
|
7894
|
-
|
|
7895
|
-
}
|
|
7896
|
-
function compilePatternConstraint(pattern) {
|
|
7897
|
-
if (nativeRegexSyntaxError(pattern)) {
|
|
7898
|
-
return { kind: "invalid" };
|
|
7940
|
+
seenIndexableFieldPaths.add(foldedPath);
|
|
7899
7941
|
}
|
|
7900
|
-
const
|
|
7901
|
-
|
|
7902
|
-
}
|
|
7903
|
-
|
|
7904
|
-
|
|
7905
|
-
|
|
7906
|
-
|
|
7907
|
-
|
|
7908
|
-
|
|
7909
|
-
|
|
7910
|
-
|
|
7911
|
-
|
|
7912
|
-
|
|
7913
|
-
|
|
7914
|
-
|
|
7915
|
-
|
|
7916
|
-
break;
|
|
7917
|
-
case "wref":
|
|
7918
|
-
if (typeof value !== "string") {
|
|
7919
|
-
errors.push(`Field "${path}" expected string, got ${typeof value}`);
|
|
7920
|
-
}
|
|
7921
|
-
break;
|
|
7922
|
-
case "boolean":
|
|
7923
|
-
if (typeof value !== "boolean") {
|
|
7924
|
-
errors.push(`Field "${path}" expected boolean, got ${typeof value}`);
|
|
7925
|
-
}
|
|
7926
|
-
break;
|
|
7927
|
-
case "array":
|
|
7928
|
-
if (!Array.isArray(value)) {
|
|
7929
|
-
errors.push(`Field "${path}" expected array, got ${typeof value}`);
|
|
7930
|
-
}
|
|
7931
|
-
break;
|
|
7942
|
+
for (const fieldPath of duplicateIndexableFieldPaths) {
|
|
7943
|
+
errors.push(
|
|
7944
|
+
`Shape declares ambiguous indexable field path "${escapeFieldNameForDisplay(fieldPath)}"; dotted field names, optional-key suffixes, and case-insensitive folded paths must not collide with another indexable path`
|
|
7945
|
+
);
|
|
7946
|
+
}
|
|
7947
|
+
if (indexableFieldCount > MAX_INDEXABLE_SCALAR_FIELDS_PER_SHAPE) {
|
|
7948
|
+
errors.push(
|
|
7949
|
+
`Shape declares ${indexableFieldCount} indexable scalar fields; maximum is ${MAX_INDEXABLE_SCALAR_FIELDS_PER_SHAPE}`
|
|
7950
|
+
);
|
|
7951
|
+
}
|
|
7952
|
+
if ("description" in obj && typeof obj.description !== "string") {
|
|
7953
|
+
errors.push('"description" must be a string');
|
|
7954
|
+
}
|
|
7955
|
+
validatePersistedStringFieldLimits(obj, void 0, errors);
|
|
7956
|
+
if (errors.length > 0) {
|
|
7957
|
+
return { valid: false, errors };
|
|
7932
7958
|
}
|
|
7959
|
+
return { valid: true };
|
|
7933
7960
|
}
|
|
7934
7961
|
|
|
7935
7962
|
// ../rules/src/system-components/system.ts
|
|
@@ -8011,7 +8038,7 @@ var collectionTypes2 = /* @__PURE__ */ new Set([
|
|
|
8011
8038
|
"list"
|
|
8012
8039
|
]);
|
|
8013
8040
|
function normalizeBackendCollectionAdd(operation, source) {
|
|
8014
|
-
const diagnostics =
|
|
8041
|
+
const diagnostics = preflightOpDiagnostics2(
|
|
8015
8042
|
{
|
|
8016
8043
|
operation: "add",
|
|
8017
8044
|
kind: "collection",
|
|
@@ -8166,21 +8193,72 @@ function toBackendStreamOperation(operation) {
|
|
|
8166
8193
|
...skipExisting === true ? { skipExisting } : {}
|
|
8167
8194
|
};
|
|
8168
8195
|
}
|
|
8169
|
-
if (operation.data === void 0) {
|
|
8170
|
-
throw new Error(`${kind} add operation requires 'data'`);
|
|
8171
|
-
}
|
|
8172
|
-
return {
|
|
8173
|
-
operation: "add",
|
|
8174
|
-
kind,
|
|
8175
|
-
name: operation.name,
|
|
8176
|
-
data: operation.data,
|
|
8177
|
-
...skipExisting === true ? { skipExisting } : {}
|
|
8178
|
-
};
|
|
8179
|
-
}
|
|
8196
|
+
if (operation.data === void 0) {
|
|
8197
|
+
throw new Error(`${kind} add operation requires 'data'`);
|
|
8198
|
+
}
|
|
8199
|
+
return {
|
|
8200
|
+
operation: "add",
|
|
8201
|
+
kind,
|
|
8202
|
+
name: operation.name,
|
|
8203
|
+
data: operation.data,
|
|
8204
|
+
...skipExisting === true ? { skipExisting } : {}
|
|
8205
|
+
};
|
|
8206
|
+
}
|
|
8207
|
+
|
|
8208
|
+
// src/stream-submit-types.ts
|
|
8209
|
+
var DEFAULT_STREAM_CHUNK_SIZE = DEFAULT_STREAM_APPEND_CHUNK_SIZE;
|
|
8210
|
+
var MAX_STREAM_APPEND_OPERATION_COUNT2 = MAX_STREAM_APPEND_OPERATION_COUNT;
|
|
8211
|
+
var PartialStreamSubmissionError = class extends Error {
|
|
8212
|
+
/**
|
|
8213
|
+
* Stable error code. Always `'PARTIAL_STREAM_SUBMISSION'`.
|
|
8214
|
+
*/
|
|
8215
|
+
code = "PARTIAL_STREAM_SUBMISSION";
|
|
8216
|
+
/**
|
|
8217
|
+
* Operations the SDK confirmed completed before the ambiguous or failing
|
|
8218
|
+
* append. The remaining operations may or may not have landed; inspect
|
|
8219
|
+
* repository state before retrying manually.
|
|
8220
|
+
*/
|
|
8221
|
+
completedOperations;
|
|
8222
|
+
/**
|
|
8223
|
+
* Underlying error that triggered the partial submission, preserved for
|
|
8224
|
+
* diagnostics. Inspect via `isWarmHubError(err.cause)` / `err.cause.kind`.
|
|
8225
|
+
*/
|
|
8226
|
+
cause;
|
|
8227
|
+
constructor(input) {
|
|
8228
|
+
super(
|
|
8229
|
+
"Stream submission failed. A timed-out append may already have landed, so inspect repository state before sending any further operations."
|
|
8230
|
+
);
|
|
8231
|
+
this.name = "WarmHubError";
|
|
8232
|
+
this.cause = input.cause;
|
|
8233
|
+
this.completedOperations = input.completedOperations;
|
|
8234
|
+
}
|
|
8235
|
+
};
|
|
8236
|
+
var AllStreamOperationsFailedError = class extends Error {
|
|
8237
|
+
code = "STREAM_ALL_OPERATIONS_FAILED";
|
|
8238
|
+
result;
|
|
8239
|
+
operations;
|
|
8240
|
+
statusCounts;
|
|
8241
|
+
cause;
|
|
8242
|
+
constructor(result) {
|
|
8243
|
+
const primaryFailure = result.operations.find(
|
|
8244
|
+
(operation) => operation.error !== void 0
|
|
8245
|
+
)?.error;
|
|
8246
|
+
super(
|
|
8247
|
+
primaryFailure ? `All ${result.operationCount} stream operations failed: ${primaryFailure.message}` : `All ${result.operationCount} stream operations failed.`
|
|
8248
|
+
);
|
|
8249
|
+
this.name = "WarmHubError";
|
|
8250
|
+
this.cause = primaryFailure;
|
|
8251
|
+
this.result = result;
|
|
8252
|
+
this.operations = result.operations;
|
|
8253
|
+
this.statusCounts = result.statusCounts ?? {
|
|
8254
|
+
applied: 0,
|
|
8255
|
+
noop: 0,
|
|
8256
|
+
error: result.operationCount
|
|
8257
|
+
};
|
|
8258
|
+
}
|
|
8259
|
+
};
|
|
8180
8260
|
|
|
8181
|
-
// src/stream-submit.ts
|
|
8182
|
-
var DEFAULT_STREAM_CHUNK_SIZE = DEFAULT_STREAM_APPEND_CHUNK_SIZE;
|
|
8183
|
-
var MAX_STREAM_APPEND_OPERATION_COUNT2 = MAX_STREAM_APPEND_OPERATION_COUNT;
|
|
8261
|
+
// src/stream-submit-utils.ts
|
|
8184
8262
|
function streamAppendResultStatus(result) {
|
|
8185
8263
|
if (result.status === "failed") {
|
|
8186
8264
|
return "error";
|
|
@@ -8232,6 +8310,85 @@ function resolveRetryPolicy(retry) {
|
|
|
8232
8310
|
)
|
|
8233
8311
|
};
|
|
8234
8312
|
}
|
|
8313
|
+
var DEFINITE_CLIENT_REJECT_CODES = /* @__PURE__ */ new Set([
|
|
8314
|
+
"UNAUTHENTICATED",
|
|
8315
|
+
"FORBIDDEN",
|
|
8316
|
+
"VALIDATION_ERROR",
|
|
8317
|
+
"SHAPE_MISMATCH",
|
|
8318
|
+
"RESERVED_NAME",
|
|
8319
|
+
"ILLEGAL_OP_SEQUENCE",
|
|
8320
|
+
"NOT_FOUND",
|
|
8321
|
+
"KIND_MISMATCH",
|
|
8322
|
+
"CONFLICT",
|
|
8323
|
+
"ALREADY_RETRACTED",
|
|
8324
|
+
"ARCHIVED",
|
|
8325
|
+
// Pre-apply rejections from stream/service.ts: applyRateLimit() and the
|
|
8326
|
+
// unresolved-token check both throw before any operation lands, so the
|
|
8327
|
+
// first-chunk wrapper would discard structured retry/validation detail
|
|
8328
|
+
// (retryAfter, the failing token, etc.).
|
|
8329
|
+
"RATE_LIMITED",
|
|
8330
|
+
"UNRESOLVED_TOKEN"
|
|
8331
|
+
]);
|
|
8332
|
+
function extractErrorCode(cause) {
|
|
8333
|
+
if (!cause || typeof cause !== "object") return void 0;
|
|
8334
|
+
const direct = cause.code;
|
|
8335
|
+
if (typeof direct === "string") return direct;
|
|
8336
|
+
const data = cause.data;
|
|
8337
|
+
const wh = data?.warmhub?.code;
|
|
8338
|
+
if (typeof wh === "string") return wh;
|
|
8339
|
+
const dc = data?.code;
|
|
8340
|
+
if (typeof dc === "string") return dc;
|
|
8341
|
+
return void 0;
|
|
8342
|
+
}
|
|
8343
|
+
function isDefiniteClientError(cause) {
|
|
8344
|
+
const code = extractErrorCode(cause);
|
|
8345
|
+
return code !== void 0 && DEFINITE_CLIENT_REJECT_CODES.has(code);
|
|
8346
|
+
}
|
|
8347
|
+
function isTransientStreamFailure(cause) {
|
|
8348
|
+
if (isDefiniteClientError(cause)) return false;
|
|
8349
|
+
if (isFetchNetworkTypeError(cause)) return true;
|
|
8350
|
+
if (cause instanceof TypeError) return false;
|
|
8351
|
+
if (cause instanceof SyntaxError) return false;
|
|
8352
|
+
const inner = cause?.cause;
|
|
8353
|
+
if (isFetchNetworkTypeError(inner)) return true;
|
|
8354
|
+
if (inner instanceof TypeError) return false;
|
|
8355
|
+
if (inner instanceof SyntaxError) return false;
|
|
8356
|
+
return true;
|
|
8357
|
+
}
|
|
8358
|
+
function isFetchNetworkTypeError(cause) {
|
|
8359
|
+
return cause instanceof TypeError && /fetch/i.test(cause.message);
|
|
8360
|
+
}
|
|
8361
|
+
function computeBackoffDelayMs(attempt, policy) {
|
|
8362
|
+
const exponential = policy.baseDelayMs * 2 ** Math.max(0, attempt - 1);
|
|
8363
|
+
const jitter = Math.random() * policy.baseDelayMs;
|
|
8364
|
+
return Math.min(policy.maxDelayMs, exponential + jitter);
|
|
8365
|
+
}
|
|
8366
|
+
function sleep(ms) {
|
|
8367
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
8368
|
+
}
|
|
8369
|
+
var TOKEN_REF_REGEX = /[$#]\d+/;
|
|
8370
|
+
function stringHasTokenRef(value) {
|
|
8371
|
+
return typeof value === "string" && TOKEN_REF_REGEX.test(value);
|
|
8372
|
+
}
|
|
8373
|
+
function structuralValueHasTokenRef(value, seen = /* @__PURE__ */ new WeakSet()) {
|
|
8374
|
+
if (stringHasTokenRef(value)) return true;
|
|
8375
|
+
if (value !== null && typeof value === "object") {
|
|
8376
|
+
if (seen.has(value)) return false;
|
|
8377
|
+
seen.add(value);
|
|
8378
|
+
if (Array.isArray(value)) {
|
|
8379
|
+
return value.some((entry) => structuralValueHasTokenRef(entry, seen));
|
|
8380
|
+
}
|
|
8381
|
+
return Object.values(value).some(
|
|
8382
|
+
(entry) => structuralValueHasTokenRef(entry, seen)
|
|
8383
|
+
);
|
|
8384
|
+
}
|
|
8385
|
+
return false;
|
|
8386
|
+
}
|
|
8387
|
+
function opUsesTokens(op) {
|
|
8388
|
+
return stringHasTokenRef(op.name) || stringHasTokenRef(op.wref) || structuralValueHasTokenRef(op.about) || stringHasTokenRef(op.aboutWref) || structuralValueHasTokenRef(op.members) || structuralValueHasTokenRef(op.data);
|
|
8389
|
+
}
|
|
8390
|
+
|
|
8391
|
+
// src/stream-submit-submit.ts
|
|
8235
8392
|
var StreamValidationError = class extends Error {
|
|
8236
8393
|
code = "VALIDATION_ERROR";
|
|
8237
8394
|
status = 400;
|
|
@@ -8240,55 +8397,6 @@ var StreamValidationError = class extends Error {
|
|
|
8240
8397
|
this.name = "WarmHubError";
|
|
8241
8398
|
}
|
|
8242
8399
|
};
|
|
8243
|
-
var PartialStreamSubmissionError = class extends Error {
|
|
8244
|
-
/**
|
|
8245
|
-
* Stable error code. Always `'PARTIAL_STREAM_SUBMISSION'`.
|
|
8246
|
-
*/
|
|
8247
|
-
code = "PARTIAL_STREAM_SUBMISSION";
|
|
8248
|
-
/**
|
|
8249
|
-
* Operations the SDK confirmed completed before the ambiguous or failing
|
|
8250
|
-
* append. The remaining operations may or may not have landed; inspect
|
|
8251
|
-
* repository state before retrying manually.
|
|
8252
|
-
*/
|
|
8253
|
-
completedOperations;
|
|
8254
|
-
/**
|
|
8255
|
-
* Underlying error that triggered the partial submission, preserved for
|
|
8256
|
-
* diagnostics. Inspect via `isWarmHubError(err.cause)` / `err.cause.kind`.
|
|
8257
|
-
*/
|
|
8258
|
-
cause;
|
|
8259
|
-
constructor(input) {
|
|
8260
|
-
super(
|
|
8261
|
-
"Stream submission failed. A timed-out append may already have landed, so inspect repository state before sending any further operations."
|
|
8262
|
-
);
|
|
8263
|
-
this.name = "WarmHubError";
|
|
8264
|
-
this.cause = input.cause;
|
|
8265
|
-
this.completedOperations = input.completedOperations;
|
|
8266
|
-
}
|
|
8267
|
-
};
|
|
8268
|
-
var AllStreamOperationsFailedError = class extends Error {
|
|
8269
|
-
code = "STREAM_ALL_OPERATIONS_FAILED";
|
|
8270
|
-
result;
|
|
8271
|
-
operations;
|
|
8272
|
-
statusCounts;
|
|
8273
|
-
cause;
|
|
8274
|
-
constructor(result) {
|
|
8275
|
-
const primaryFailure = result.operations.find(
|
|
8276
|
-
(operation) => operation.error !== void 0
|
|
8277
|
-
)?.error;
|
|
8278
|
-
super(
|
|
8279
|
-
primaryFailure ? `All ${result.operationCount} stream operations failed: ${primaryFailure.message}` : `All ${result.operationCount} stream operations failed.`
|
|
8280
|
-
);
|
|
8281
|
-
this.name = "WarmHubError";
|
|
8282
|
-
this.cause = primaryFailure;
|
|
8283
|
-
this.result = result;
|
|
8284
|
-
this.operations = result.operations;
|
|
8285
|
-
this.statusCounts = result.statusCounts ?? {
|
|
8286
|
-
applied: 0,
|
|
8287
|
-
noop: 0,
|
|
8288
|
-
error: result.operationCount
|
|
8289
|
-
};
|
|
8290
|
-
}
|
|
8291
|
-
};
|
|
8292
8400
|
async function submitOperationsViaStream(client, args) {
|
|
8293
8401
|
if (args.operations.length === 0) {
|
|
8294
8402
|
throw new StreamValidationError(
|
|
@@ -8362,138 +8470,61 @@ async function submitOperationsViaStream(client, args) {
|
|
|
8362
8470
|
toSubmittedStreamResult(
|
|
8363
8471
|
{
|
|
8364
8472
|
operationCount: chunkResults.length
|
|
8365
|
-
},
|
|
8366
|
-
args.committer,
|
|
8367
|
-
createdByEmail,
|
|
8368
|
-
args.message,
|
|
8369
|
-
chunkResults,
|
|
8370
|
-
operations
|
|
8371
|
-
)
|
|
8372
|
-
);
|
|
8373
|
-
throw new PartialStreamSubmissionError({
|
|
8374
|
-
cause,
|
|
8375
|
-
completedOperations
|
|
8376
|
-
});
|
|
8377
|
-
}
|
|
8378
|
-
}
|
|
8379
|
-
}
|
|
8380
|
-
const result = toSubmittedStreamResult(
|
|
8381
|
-
{
|
|
8382
|
-
operationCount: chunkResults.length
|
|
8383
|
-
},
|
|
8384
|
-
args.committer,
|
|
8385
|
-
createdByEmail,
|
|
8386
|
-
args.message,
|
|
8387
|
-
chunkResults,
|
|
8388
|
-
operations
|
|
8389
|
-
);
|
|
8390
|
-
if (isAllSubmittedOperationsFailed(result, operations.length)) {
|
|
8391
|
-
const failure = new AllStreamOperationsFailedError(result);
|
|
8392
|
-
if (sawAmbiguousAttempt) {
|
|
8393
|
-
throw new PartialStreamSubmissionError({
|
|
8394
|
-
cause: failure,
|
|
8395
|
-
completedOperations: completedOperationsFrom(result)
|
|
8396
|
-
});
|
|
8397
|
-
}
|
|
8398
|
-
throw failure;
|
|
8399
|
-
}
|
|
8400
|
-
return result;
|
|
8401
|
-
}
|
|
8402
|
-
function isAllSubmittedOperationsFailed(result, submittedOperationCount) {
|
|
8403
|
-
if (submittedOperationCount <= 0) {
|
|
8404
|
-
return false;
|
|
8405
|
-
}
|
|
8406
|
-
const inputRowsFailed = /* @__PURE__ */ new Map();
|
|
8407
|
-
for (const operation of result.operations) {
|
|
8408
|
-
if (operation.opIndex === void 0 || operation.opIndex < 0 || operation.opIndex >= submittedOperationCount) {
|
|
8409
|
-
continue;
|
|
8410
|
-
}
|
|
8411
|
-
inputRowsFailed.set(
|
|
8412
|
-
operation.opIndex,
|
|
8413
|
-
(inputRowsFailed.get(operation.opIndex) ?? true) && operation.status === "error"
|
|
8414
|
-
);
|
|
8415
|
-
}
|
|
8416
|
-
return inputRowsFailed.size === submittedOperationCount && [...inputRowsFailed.values()].every(Boolean);
|
|
8417
|
-
}
|
|
8418
|
-
function completedOperationsFrom(result) {
|
|
8419
|
-
return result.operations.filter((operation) => operation.status !== "error");
|
|
8420
|
-
}
|
|
8421
|
-
var DEFINITE_CLIENT_REJECT_CODES = /* @__PURE__ */ new Set([
|
|
8422
|
-
"UNAUTHENTICATED",
|
|
8423
|
-
"FORBIDDEN",
|
|
8424
|
-
"VALIDATION_ERROR",
|
|
8425
|
-
"SHAPE_MISMATCH",
|
|
8426
|
-
"RESERVED_NAME",
|
|
8427
|
-
"ILLEGAL_OP_SEQUENCE",
|
|
8428
|
-
"NOT_FOUND",
|
|
8429
|
-
"KIND_MISMATCH",
|
|
8430
|
-
"CONFLICT",
|
|
8431
|
-
"ALREADY_RETRACTED",
|
|
8432
|
-
"ARCHIVED",
|
|
8433
|
-
// Pre-apply rejections from stream/service.ts: applyRateLimit() and the
|
|
8434
|
-
// unresolved-token check both throw before any operation lands, so the
|
|
8435
|
-
// first-chunk wrapper would discard structured retry/validation detail
|
|
8436
|
-
// (retryAfter, the failing token, etc.).
|
|
8437
|
-
"RATE_LIMITED",
|
|
8438
|
-
"UNRESOLVED_TOKEN"
|
|
8439
|
-
]);
|
|
8440
|
-
function extractErrorCode(cause) {
|
|
8441
|
-
if (!cause || typeof cause !== "object") return void 0;
|
|
8442
|
-
const direct = cause.code;
|
|
8443
|
-
if (typeof direct === "string") return direct;
|
|
8444
|
-
const data = cause.data;
|
|
8445
|
-
const wh = data?.warmhub?.code;
|
|
8446
|
-
if (typeof wh === "string") return wh;
|
|
8447
|
-
const dc = data?.code;
|
|
8448
|
-
if (typeof dc === "string") return dc;
|
|
8449
|
-
return void 0;
|
|
8450
|
-
}
|
|
8451
|
-
function isDefiniteClientError(cause) {
|
|
8452
|
-
const code = extractErrorCode(cause);
|
|
8453
|
-
return code !== void 0 && DEFINITE_CLIENT_REJECT_CODES.has(code);
|
|
8454
|
-
}
|
|
8455
|
-
function isTransientStreamFailure(cause) {
|
|
8456
|
-
if (isDefiniteClientError(cause)) return false;
|
|
8457
|
-
if (isFetchNetworkTypeError(cause)) return true;
|
|
8458
|
-
if (cause instanceof TypeError) return false;
|
|
8459
|
-
if (cause instanceof SyntaxError) return false;
|
|
8460
|
-
const inner = cause?.cause;
|
|
8461
|
-
if (isFetchNetworkTypeError(inner)) return true;
|
|
8462
|
-
if (inner instanceof TypeError) return false;
|
|
8463
|
-
if (inner instanceof SyntaxError) return false;
|
|
8464
|
-
return true;
|
|
8465
|
-
}
|
|
8466
|
-
function isFetchNetworkTypeError(cause) {
|
|
8467
|
-
return cause instanceof TypeError && /fetch/i.test(cause.message);
|
|
8468
|
-
}
|
|
8469
|
-
function computeBackoffDelayMs(attempt, policy) {
|
|
8470
|
-
const exponential = policy.baseDelayMs * 2 ** Math.max(0, attempt - 1);
|
|
8471
|
-
const jitter = Math.random() * policy.baseDelayMs;
|
|
8472
|
-
return Math.min(policy.maxDelayMs, exponential + jitter);
|
|
8473
|
-
}
|
|
8474
|
-
function sleep(ms) {
|
|
8475
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
8476
|
-
}
|
|
8477
|
-
var TOKEN_REF_REGEX = /[$#]\d+/;
|
|
8478
|
-
function stringHasTokenRef(value) {
|
|
8479
|
-
return typeof value === "string" && TOKEN_REF_REGEX.test(value);
|
|
8473
|
+
},
|
|
8474
|
+
args.committer,
|
|
8475
|
+
createdByEmail,
|
|
8476
|
+
args.message,
|
|
8477
|
+
chunkResults,
|
|
8478
|
+
operations
|
|
8479
|
+
)
|
|
8480
|
+
);
|
|
8481
|
+
throw new PartialStreamSubmissionError({
|
|
8482
|
+
cause,
|
|
8483
|
+
completedOperations
|
|
8484
|
+
});
|
|
8485
|
+
}
|
|
8486
|
+
}
|
|
8487
|
+
}
|
|
8488
|
+
const result = toSubmittedStreamResult(
|
|
8489
|
+
{
|
|
8490
|
+
operationCount: chunkResults.length
|
|
8491
|
+
},
|
|
8492
|
+
args.committer,
|
|
8493
|
+
createdByEmail,
|
|
8494
|
+
args.message,
|
|
8495
|
+
chunkResults,
|
|
8496
|
+
operations
|
|
8497
|
+
);
|
|
8498
|
+
if (isAllSubmittedOperationsFailed(result, operations.length)) {
|
|
8499
|
+
const failure = new AllStreamOperationsFailedError(result);
|
|
8500
|
+
if (sawAmbiguousAttempt) {
|
|
8501
|
+
throw new PartialStreamSubmissionError({
|
|
8502
|
+
cause: failure,
|
|
8503
|
+
completedOperations: completedOperationsFrom(result)
|
|
8504
|
+
});
|
|
8505
|
+
}
|
|
8506
|
+
throw failure;
|
|
8507
|
+
}
|
|
8508
|
+
return result;
|
|
8480
8509
|
}
|
|
8481
|
-
function
|
|
8482
|
-
if (
|
|
8483
|
-
|
|
8484
|
-
|
|
8485
|
-
|
|
8486
|
-
|
|
8487
|
-
|
|
8510
|
+
function isAllSubmittedOperationsFailed(result, submittedOperationCount) {
|
|
8511
|
+
if (submittedOperationCount <= 0) {
|
|
8512
|
+
return false;
|
|
8513
|
+
}
|
|
8514
|
+
const inputRowsFailed = /* @__PURE__ */ new Map();
|
|
8515
|
+
for (const operation of result.operations) {
|
|
8516
|
+
if (operation.opIndex === void 0 || operation.opIndex < 0 || operation.opIndex >= submittedOperationCount) {
|
|
8517
|
+
continue;
|
|
8488
8518
|
}
|
|
8489
|
-
|
|
8490
|
-
|
|
8519
|
+
inputRowsFailed.set(
|
|
8520
|
+
operation.opIndex,
|
|
8521
|
+
(inputRowsFailed.get(operation.opIndex) ?? true) && operation.status === "error"
|
|
8491
8522
|
);
|
|
8492
8523
|
}
|
|
8493
|
-
return
|
|
8524
|
+
return inputRowsFailed.size === submittedOperationCount && [...inputRowsFailed.values()].every(Boolean);
|
|
8494
8525
|
}
|
|
8495
|
-
function
|
|
8496
|
-
return
|
|
8526
|
+
function completedOperationsFrom(result) {
|
|
8527
|
+
return result.operations.filter((operation) => operation.status !== "error");
|
|
8497
8528
|
}
|
|
8498
8529
|
function createStreamId() {
|
|
8499
8530
|
return globalThis.crypto?.randomUUID?.() ?? `stream-${Date.now()}-${Math.random()}`;
|
|
@@ -8697,39 +8728,234 @@ async function verifyCliCall(request, secrets, opts) {
|
|
|
8697
8728
|
);
|
|
8698
8729
|
}
|
|
8699
8730
|
}
|
|
8700
|
-
return { method: cliMethod, installRepo, args };
|
|
8701
|
-
}
|
|
8702
|
-
function isNonEmpty(value) {
|
|
8703
|
-
return typeof value === "string" && value.length > 0;
|
|
8704
|
-
}
|
|
8705
|
-
function isArgsRecord(value) {
|
|
8706
|
-
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
8707
|
-
return false;
|
|
8731
|
+
return { method: cliMethod, installRepo, args };
|
|
8732
|
+
}
|
|
8733
|
+
function isNonEmpty(value) {
|
|
8734
|
+
return typeof value === "string" && value.length > 0;
|
|
8735
|
+
}
|
|
8736
|
+
function isArgsRecord(value) {
|
|
8737
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
8738
|
+
return false;
|
|
8739
|
+
}
|
|
8740
|
+
for (const v of Object.values(value)) {
|
|
8741
|
+
if (v === void 0) continue;
|
|
8742
|
+
if (typeof v === "string") continue;
|
|
8743
|
+
if (typeof v === "number") continue;
|
|
8744
|
+
if (typeof v === "boolean") continue;
|
|
8745
|
+
return false;
|
|
8746
|
+
}
|
|
8747
|
+
return true;
|
|
8748
|
+
}
|
|
8749
|
+
async function constantTimeEqual(a, b) {
|
|
8750
|
+
const [aDigest, bDigest] = await Promise.all([sha256(a), sha256(b)]);
|
|
8751
|
+
let diff = 0;
|
|
8752
|
+
for (let i = 0; i < aDigest.length; i++) {
|
|
8753
|
+
diff |= aDigest[i] ^ bDigest[i];
|
|
8754
|
+
}
|
|
8755
|
+
return diff === 0;
|
|
8756
|
+
}
|
|
8757
|
+
async function sha256(value) {
|
|
8758
|
+
return new Uint8Array(
|
|
8759
|
+
await crypto.subtle.digest("SHA-256", new TextEncoder().encode(value))
|
|
8760
|
+
);
|
|
8761
|
+
}
|
|
8762
|
+
|
|
8763
|
+
// src/operation-builder-helpers.ts
|
|
8764
|
+
function inferShapeFromLocalPath(name) {
|
|
8765
|
+
if (!name.includes("/")) return void 0;
|
|
8766
|
+
return name.split("/")[0];
|
|
8767
|
+
}
|
|
8768
|
+
function inferShapeForAdd(op) {
|
|
8769
|
+
if ("shapeWref" in op && typeof op.shapeWref === "string") {
|
|
8770
|
+
return op.shapeWref;
|
|
8771
|
+
}
|
|
8772
|
+
if (op.name) {
|
|
8773
|
+
return inferShapeFromLocalPath(op.name);
|
|
8774
|
+
}
|
|
8775
|
+
return void 0;
|
|
8776
|
+
}
|
|
8777
|
+
function asNonEmptyString(value) {
|
|
8778
|
+
if (typeof value !== "string") return void 0;
|
|
8779
|
+
const trimmed = value.trim();
|
|
8780
|
+
return trimmed.length > 0 ? trimmed : void 0;
|
|
8781
|
+
}
|
|
8782
|
+
function normalizeAddKind(op) {
|
|
8783
|
+
return inferOperationKind({
|
|
8784
|
+
kind: op.kind,
|
|
8785
|
+
name: op.name,
|
|
8786
|
+
about: getAddAbout(op),
|
|
8787
|
+
type: getCollectionType(op),
|
|
8788
|
+
members: getCollectionMembers(op)
|
|
8789
|
+
});
|
|
8790
|
+
}
|
|
8791
|
+
function getAddAbout(op) {
|
|
8792
|
+
if ("about" in op && op.about !== void 0) return op.about;
|
|
8793
|
+
if ("aboutWref" in op) return op.aboutWref;
|
|
8794
|
+
return void 0;
|
|
8795
|
+
}
|
|
8796
|
+
function getAddData(op) {
|
|
8797
|
+
return "data" in op ? op.data : void 0;
|
|
8798
|
+
}
|
|
8799
|
+
function getCollectionType(op) {
|
|
8800
|
+
return "type" in op ? op.type : void 0;
|
|
8801
|
+
}
|
|
8802
|
+
function getCollectionMembers(op) {
|
|
8803
|
+
return "members" in op ? op.members : void 0;
|
|
8804
|
+
}
|
|
8805
|
+
function normalizeReviseKind(op, name) {
|
|
8806
|
+
return inferOperationKind({ kind: op.kind, name });
|
|
8807
|
+
}
|
|
8808
|
+
function toBackendOp(op) {
|
|
8809
|
+
if (op.operation === "retract") {
|
|
8810
|
+
return {
|
|
8811
|
+
operation: "retract",
|
|
8812
|
+
name: op.name,
|
|
8813
|
+
...op.kind ? { kind: op.kind } : {},
|
|
8814
|
+
...op.reason ? { reason: op.reason } : {},
|
|
8815
|
+
...op.leaseId ? { leaseId: op.leaseId } : {}
|
|
8816
|
+
};
|
|
8817
|
+
}
|
|
8818
|
+
if (op.operation === "add") {
|
|
8819
|
+
const kind2 = normalizeAddKind(op);
|
|
8820
|
+
if (kind2 === "collection") {
|
|
8821
|
+
return normalizeBackendCollectionAdd(op, "OperationBuilder");
|
|
8822
|
+
}
|
|
8823
|
+
const addOp = op;
|
|
8824
|
+
const name2 = asNonEmptyString(addOp.name);
|
|
8825
|
+
if (!name2) {
|
|
8826
|
+
throw new Error("OperationBuilder: add operation missing name");
|
|
8827
|
+
}
|
|
8828
|
+
if (kind2 === "assertion") {
|
|
8829
|
+
const about = getAddAbout(addOp);
|
|
8830
|
+
if (about === void 0) {
|
|
8831
|
+
throw new Error(
|
|
8832
|
+
`OperationBuilder: assertion add '${name2}' missing required 'about'`
|
|
8833
|
+
);
|
|
8834
|
+
}
|
|
8835
|
+
return {
|
|
8836
|
+
operation: "add",
|
|
8837
|
+
kind: kind2,
|
|
8838
|
+
name: name2,
|
|
8839
|
+
about,
|
|
8840
|
+
data: addOp.data ?? {},
|
|
8841
|
+
...addOp.skipExisting === true ? { skipExisting: true } : {}
|
|
8842
|
+
};
|
|
8843
|
+
}
|
|
8844
|
+
return {
|
|
8845
|
+
operation: "add",
|
|
8846
|
+
kind: kind2,
|
|
8847
|
+
name: name2,
|
|
8848
|
+
data: addOp.data ?? {},
|
|
8849
|
+
...addOp.skipExisting === true ? { skipExisting: true } : {}
|
|
8850
|
+
};
|
|
8851
|
+
}
|
|
8852
|
+
const name = asNonEmptyString(op.name) ?? asNonEmptyString(op.wref);
|
|
8853
|
+
if (!name) {
|
|
8854
|
+
throw new Error(
|
|
8855
|
+
"OperationBuilder: revise operation missing required target ('name' or 'wref')"
|
|
8856
|
+
);
|
|
8857
|
+
}
|
|
8858
|
+
const kind = normalizeReviseKind(op, name);
|
|
8859
|
+
if (kind === "collection") {
|
|
8860
|
+
throw new Error(
|
|
8861
|
+
`OperationBuilder: collection revise is no longer supported \u2014 use retract('${name}') instead`
|
|
8862
|
+
);
|
|
8863
|
+
}
|
|
8864
|
+
return {
|
|
8865
|
+
operation: "revise",
|
|
8866
|
+
kind,
|
|
8867
|
+
name,
|
|
8868
|
+
data: op.data ?? {},
|
|
8869
|
+
...op.expectedVersion !== void 0 ? { expectedVersion: op.expectedVersion } : {},
|
|
8870
|
+
...op.leaseId ? { leaseId: op.leaseId } : {}
|
|
8871
|
+
};
|
|
8872
|
+
}
|
|
8873
|
+
function toPreflightOp(op, kind) {
|
|
8874
|
+
if (op.operation === "add") {
|
|
8875
|
+
return {
|
|
8876
|
+
operation: op.operation,
|
|
8877
|
+
kind,
|
|
8878
|
+
name: op.name,
|
|
8879
|
+
about: getAddAbout(op),
|
|
8880
|
+
type: getCollectionType(op),
|
|
8881
|
+
members: getCollectionMembers(op)
|
|
8882
|
+
};
|
|
8883
|
+
}
|
|
8884
|
+
return {
|
|
8885
|
+
operation: op.operation,
|
|
8886
|
+
kind,
|
|
8887
|
+
name: op.name ?? op.wref
|
|
8888
|
+
};
|
|
8889
|
+
}
|
|
8890
|
+
function throwAggregatedMessages(messages) {
|
|
8891
|
+
if (messages.length === 0) return;
|
|
8892
|
+
throw new Error(messages.join("; "));
|
|
8893
|
+
}
|
|
8894
|
+
function preflightCheckCommon(verb, kind, name, data) {
|
|
8895
|
+
const errors = [];
|
|
8896
|
+
const requiresLocalPath = kind === "thing" || kind === "assertion" || kind === "collection" && verb === "REVISE";
|
|
8897
|
+
if (requiresLocalPath) {
|
|
8898
|
+
if (!splitLocalPath(name)) {
|
|
8899
|
+
errors.push(
|
|
8900
|
+
`${verb} ${kind} name must be a local path (Shape/name), got: "${name}"`
|
|
8901
|
+
);
|
|
8902
|
+
}
|
|
8903
|
+
}
|
|
8904
|
+
if (kind === "shape" && (verb === "ADD" || verb === "REVISE") && data !== void 0) {
|
|
8905
|
+
const preflight = shapeDefinitionPreflightError(
|
|
8906
|
+
name,
|
|
8907
|
+
data,
|
|
8908
|
+
verb === "REVISE" ? "revise" : "add"
|
|
8909
|
+
);
|
|
8910
|
+
if (preflight) errors.push(preflight);
|
|
8911
|
+
}
|
|
8912
|
+
return errors;
|
|
8913
|
+
}
|
|
8914
|
+
function preflightCheckAdd(op, kind) {
|
|
8915
|
+
const messages = [];
|
|
8916
|
+
if (op.name) {
|
|
8917
|
+
messages.push(...preflightCheckCommon("ADD", kind, op.name, getAddData(op)));
|
|
8918
|
+
}
|
|
8919
|
+
if (kind === "assertion" && getAddAbout(op) === void 0 && // Token-bearing names defer to validate() so the more specific token
|
|
8920
|
+
// diagnostics surface instead of this eager check.
|
|
8921
|
+
!(op.name && hasAnyTokens(op.name))) {
|
|
8922
|
+
messages.push(
|
|
8923
|
+
`ADD assertion '${op.name}' missing required 'about' \u2014 for hierarchical thing names set kind: 'thing' explicitly`
|
|
8924
|
+
);
|
|
8925
|
+
}
|
|
8926
|
+
if (kind !== "collection" && (getCollectionType(op) !== void 0 || getCollectionMembers(op) !== void 0)) {
|
|
8927
|
+
messages.push(
|
|
8928
|
+
`ADD '${op.name}' has collection fields but inferred kind '${kind}' \u2014 collection adds require both 'type' and 'members', or set kind: 'collection'`
|
|
8929
|
+
);
|
|
8708
8930
|
}
|
|
8709
|
-
|
|
8710
|
-
|
|
8711
|
-
|
|
8712
|
-
|
|
8713
|
-
|
|
8714
|
-
|
|
8931
|
+
if (kind === "collection") {
|
|
8932
|
+
try {
|
|
8933
|
+
normalizeBackendCollectionAdd(op, "OperationBuilder");
|
|
8934
|
+
} catch (error) {
|
|
8935
|
+
messages.push(error instanceof Error ? error.message : String(error));
|
|
8936
|
+
}
|
|
8715
8937
|
}
|
|
8716
|
-
|
|
8717
|
-
|
|
8718
|
-
async function constantTimeEqual(a, b) {
|
|
8719
|
-
const [aDigest, bDigest] = await Promise.all([sha256(a), sha256(b)]);
|
|
8720
|
-
let diff = 0;
|
|
8721
|
-
for (let i = 0; i < aDigest.length; i++) {
|
|
8722
|
-
diff |= aDigest[i] ^ bDigest[i];
|
|
8938
|
+
for (const diagnostic of preflightOpDiagnostics2(toPreflightOp(op, kind), 0)) {
|
|
8939
|
+
messages.push(diagnostic.message);
|
|
8723
8940
|
}
|
|
8724
|
-
|
|
8941
|
+
throwAggregatedMessages(messages);
|
|
8725
8942
|
}
|
|
8726
|
-
|
|
8727
|
-
|
|
8728
|
-
|
|
8943
|
+
function preflightCheckRevise(kind, name, data) {
|
|
8944
|
+
const messages = [];
|
|
8945
|
+
if (data === void 0) {
|
|
8946
|
+
messages.push(`OperationBuilder: ${kind} revise requires 'data'`);
|
|
8947
|
+
}
|
|
8948
|
+
messages.push(
|
|
8949
|
+
...preflightCheckCommon("REVISE", kind, name, data),
|
|
8950
|
+
...preflightOpDiagnostics2(
|
|
8951
|
+
toPreflightOp({ operation: "revise", name, data }, kind),
|
|
8952
|
+
0
|
|
8953
|
+
).map((diagnostic) => diagnostic.message)
|
|
8729
8954
|
);
|
|
8955
|
+
throwAggregatedMessages(messages);
|
|
8730
8956
|
}
|
|
8731
8957
|
|
|
8732
|
-
// src/operation-builder.ts
|
|
8958
|
+
// src/operation-builder-core.ts
|
|
8733
8959
|
var OperationBuilder = class {
|
|
8734
8960
|
ops = [];
|
|
8735
8961
|
pendingNames = /* @__PURE__ */ new Set();
|
|
@@ -8979,7 +9205,7 @@ var OperationBuilder = class {
|
|
|
8979
9205
|
target ? normalizeReviseKind(op, target) : "thing"
|
|
8980
9206
|
);
|
|
8981
9207
|
});
|
|
8982
|
-
const diagnostics =
|
|
9208
|
+
const diagnostics = preflightCommitDiagnostics2(preflightOps);
|
|
8983
9209
|
for (const diagnostic of diagnostics) {
|
|
8984
9210
|
errors.push({
|
|
8985
9211
|
code: diagnostic.code,
|
|
@@ -9043,199 +9269,6 @@ var OperationBuilder = class {
|
|
|
9043
9269
|
}
|
|
9044
9270
|
}
|
|
9045
9271
|
};
|
|
9046
|
-
function inferShapeFromLocalPath(name) {
|
|
9047
|
-
if (!name.includes("/")) return void 0;
|
|
9048
|
-
return name.split("/")[0];
|
|
9049
|
-
}
|
|
9050
|
-
function inferShapeForAdd(op) {
|
|
9051
|
-
if ("shapeWref" in op && typeof op.shapeWref === "string") {
|
|
9052
|
-
return op.shapeWref;
|
|
9053
|
-
}
|
|
9054
|
-
if (op.name) {
|
|
9055
|
-
return inferShapeFromLocalPath(op.name);
|
|
9056
|
-
}
|
|
9057
|
-
return void 0;
|
|
9058
|
-
}
|
|
9059
|
-
function asNonEmptyString(value) {
|
|
9060
|
-
if (typeof value !== "string") return void 0;
|
|
9061
|
-
const trimmed = value.trim();
|
|
9062
|
-
return trimmed.length > 0 ? trimmed : void 0;
|
|
9063
|
-
}
|
|
9064
|
-
function normalizeAddKind(op) {
|
|
9065
|
-
return inferOperationKind({
|
|
9066
|
-
kind: op.kind,
|
|
9067
|
-
name: op.name,
|
|
9068
|
-
about: getAddAbout(op),
|
|
9069
|
-
type: getCollectionType(op),
|
|
9070
|
-
members: getCollectionMembers(op)
|
|
9071
|
-
});
|
|
9072
|
-
}
|
|
9073
|
-
function getAddAbout(op) {
|
|
9074
|
-
if ("about" in op && op.about !== void 0) return op.about;
|
|
9075
|
-
if ("aboutWref" in op) return op.aboutWref;
|
|
9076
|
-
return void 0;
|
|
9077
|
-
}
|
|
9078
|
-
function getAddData(op) {
|
|
9079
|
-
return "data" in op ? op.data : void 0;
|
|
9080
|
-
}
|
|
9081
|
-
function getCollectionType(op) {
|
|
9082
|
-
return "type" in op ? op.type : void 0;
|
|
9083
|
-
}
|
|
9084
|
-
function getCollectionMembers(op) {
|
|
9085
|
-
return "members" in op ? op.members : void 0;
|
|
9086
|
-
}
|
|
9087
|
-
function normalizeReviseKind(op, name) {
|
|
9088
|
-
return inferOperationKind({ kind: op.kind, name });
|
|
9089
|
-
}
|
|
9090
|
-
function toBackendOp(op) {
|
|
9091
|
-
if (op.operation === "retract") {
|
|
9092
|
-
return {
|
|
9093
|
-
operation: "retract",
|
|
9094
|
-
name: op.name,
|
|
9095
|
-
...op.kind ? { kind: op.kind } : {},
|
|
9096
|
-
...op.reason ? { reason: op.reason } : {},
|
|
9097
|
-
...op.leaseId ? { leaseId: op.leaseId } : {}
|
|
9098
|
-
};
|
|
9099
|
-
}
|
|
9100
|
-
if (op.operation === "add") {
|
|
9101
|
-
const kind2 = normalizeAddKind(op);
|
|
9102
|
-
if (kind2 === "collection") {
|
|
9103
|
-
return normalizeBackendCollectionAdd(op, "OperationBuilder");
|
|
9104
|
-
}
|
|
9105
|
-
const addOp = op;
|
|
9106
|
-
const name2 = asNonEmptyString(addOp.name);
|
|
9107
|
-
if (!name2) {
|
|
9108
|
-
throw new Error("OperationBuilder: add operation missing name");
|
|
9109
|
-
}
|
|
9110
|
-
if (kind2 === "assertion") {
|
|
9111
|
-
const about = getAddAbout(addOp);
|
|
9112
|
-
if (about === void 0) {
|
|
9113
|
-
throw new Error(
|
|
9114
|
-
`OperationBuilder: assertion add '${name2}' missing required 'about'`
|
|
9115
|
-
);
|
|
9116
|
-
}
|
|
9117
|
-
return {
|
|
9118
|
-
operation: "add",
|
|
9119
|
-
kind: kind2,
|
|
9120
|
-
name: name2,
|
|
9121
|
-
about,
|
|
9122
|
-
data: addOp.data ?? {},
|
|
9123
|
-
...addOp.skipExisting === true ? { skipExisting: true } : {}
|
|
9124
|
-
};
|
|
9125
|
-
}
|
|
9126
|
-
return {
|
|
9127
|
-
operation: "add",
|
|
9128
|
-
kind: kind2,
|
|
9129
|
-
name: name2,
|
|
9130
|
-
data: addOp.data ?? {},
|
|
9131
|
-
...addOp.skipExisting === true ? { skipExisting: true } : {}
|
|
9132
|
-
};
|
|
9133
|
-
}
|
|
9134
|
-
const name = asNonEmptyString(op.name) ?? asNonEmptyString(op.wref);
|
|
9135
|
-
if (!name) {
|
|
9136
|
-
throw new Error(
|
|
9137
|
-
"OperationBuilder: revise operation missing required target ('name' or 'wref')"
|
|
9138
|
-
);
|
|
9139
|
-
}
|
|
9140
|
-
const kind = normalizeReviseKind(op, name);
|
|
9141
|
-
if (kind === "collection") {
|
|
9142
|
-
throw new Error(
|
|
9143
|
-
`OperationBuilder: collection revise is no longer supported \u2014 use retract('${name}') instead`
|
|
9144
|
-
);
|
|
9145
|
-
}
|
|
9146
|
-
return {
|
|
9147
|
-
operation: "revise",
|
|
9148
|
-
kind,
|
|
9149
|
-
name,
|
|
9150
|
-
data: op.data ?? {},
|
|
9151
|
-
...op.expectedVersion !== void 0 ? { expectedVersion: op.expectedVersion } : {},
|
|
9152
|
-
...op.leaseId ? { leaseId: op.leaseId } : {}
|
|
9153
|
-
};
|
|
9154
|
-
}
|
|
9155
|
-
function toPreflightOp(op, kind) {
|
|
9156
|
-
if (op.operation === "add") {
|
|
9157
|
-
return {
|
|
9158
|
-
operation: op.operation,
|
|
9159
|
-
kind,
|
|
9160
|
-
name: op.name,
|
|
9161
|
-
about: getAddAbout(op),
|
|
9162
|
-
type: getCollectionType(op),
|
|
9163
|
-
members: getCollectionMembers(op)
|
|
9164
|
-
};
|
|
9165
|
-
}
|
|
9166
|
-
return {
|
|
9167
|
-
operation: op.operation,
|
|
9168
|
-
kind,
|
|
9169
|
-
name: op.name ?? op.wref
|
|
9170
|
-
};
|
|
9171
|
-
}
|
|
9172
|
-
function throwAggregatedMessages(messages) {
|
|
9173
|
-
if (messages.length === 0) return;
|
|
9174
|
-
throw new Error(messages.join("; "));
|
|
9175
|
-
}
|
|
9176
|
-
function preflightCheckCommon(verb, kind, name, data) {
|
|
9177
|
-
const errors = [];
|
|
9178
|
-
const requiresLocalPath = kind === "thing" || kind === "assertion" || kind === "collection" && verb === "REVISE";
|
|
9179
|
-
if (requiresLocalPath) {
|
|
9180
|
-
if (!splitLocalPath(name)) {
|
|
9181
|
-
errors.push(
|
|
9182
|
-
`${verb} ${kind} name must be a local path (Shape/name), got: "${name}"`
|
|
9183
|
-
);
|
|
9184
|
-
}
|
|
9185
|
-
}
|
|
9186
|
-
if (kind === "shape" && (verb === "ADD" || verb === "REVISE") && data !== void 0) {
|
|
9187
|
-
const preflight = shapeDefinitionPreflightError(
|
|
9188
|
-
name,
|
|
9189
|
-
data,
|
|
9190
|
-
verb === "REVISE" ? "revise" : "add"
|
|
9191
|
-
);
|
|
9192
|
-
if (preflight) errors.push(preflight);
|
|
9193
|
-
}
|
|
9194
|
-
return errors;
|
|
9195
|
-
}
|
|
9196
|
-
function preflightCheckAdd(op, kind) {
|
|
9197
|
-
const messages = [];
|
|
9198
|
-
if (op.name) {
|
|
9199
|
-
messages.push(...preflightCheckCommon("ADD", kind, op.name, getAddData(op)));
|
|
9200
|
-
}
|
|
9201
|
-
if (kind === "assertion" && getAddAbout(op) === void 0 && // Token-bearing names defer to validate() so the more specific token
|
|
9202
|
-
// diagnostics surface instead of this eager check.
|
|
9203
|
-
!(op.name && hasAnyTokens(op.name))) {
|
|
9204
|
-
messages.push(
|
|
9205
|
-
`ADD assertion '${op.name}' missing required 'about' \u2014 for hierarchical thing names set kind: 'thing' explicitly`
|
|
9206
|
-
);
|
|
9207
|
-
}
|
|
9208
|
-
if (kind !== "collection" && (getCollectionType(op) !== void 0 || getCollectionMembers(op) !== void 0)) {
|
|
9209
|
-
messages.push(
|
|
9210
|
-
`ADD '${op.name}' has collection fields but inferred kind '${kind}' \u2014 collection adds require both 'type' and 'members', or set kind: 'collection'`
|
|
9211
|
-
);
|
|
9212
|
-
}
|
|
9213
|
-
if (kind === "collection") {
|
|
9214
|
-
try {
|
|
9215
|
-
normalizeBackendCollectionAdd(op, "OperationBuilder");
|
|
9216
|
-
} catch (error) {
|
|
9217
|
-
messages.push(error instanceof Error ? error.message : String(error));
|
|
9218
|
-
}
|
|
9219
|
-
}
|
|
9220
|
-
for (const diagnostic of preflightOpDiagnostics(toPreflightOp(op, kind), 0)) {
|
|
9221
|
-
messages.push(diagnostic.message);
|
|
9222
|
-
}
|
|
9223
|
-
throwAggregatedMessages(messages);
|
|
9224
|
-
}
|
|
9225
|
-
function preflightCheckRevise(kind, name, data) {
|
|
9226
|
-
const messages = [];
|
|
9227
|
-
if (data === void 0) {
|
|
9228
|
-
messages.push(`OperationBuilder: ${kind} revise requires 'data'`);
|
|
9229
|
-
}
|
|
9230
|
-
messages.push(
|
|
9231
|
-
...preflightCheckCommon("REVISE", kind, name, data),
|
|
9232
|
-
...preflightOpDiagnostics(
|
|
9233
|
-
toPreflightOp({ operation: "revise", name, data }, kind),
|
|
9234
|
-
0
|
|
9235
|
-
).map((diagnostic) => diagnostic.message)
|
|
9236
|
-
);
|
|
9237
|
-
throwAggregatedMessages(messages);
|
|
9238
|
-
}
|
|
9239
9272
|
|
|
9240
9273
|
// src/index.ts
|
|
9241
9274
|
function normalizeWref(wref) {
|
|
@@ -9246,7 +9279,8 @@ var WARMHUB_CLIENT_OPTION_NAMES = [
|
|
|
9246
9279
|
"fetch",
|
|
9247
9280
|
"accessToken",
|
|
9248
9281
|
"auth",
|
|
9249
|
-
"functionLogs"
|
|
9282
|
+
"functionLogs",
|
|
9283
|
+
"client"
|
|
9250
9284
|
];
|
|
9251
9285
|
var WARMHUB_CLIENT_OPTION_NAME_SET = new Set(
|
|
9252
9286
|
WARMHUB_CLIENT_OPTION_NAMES
|
|
@@ -9264,7 +9298,7 @@ function validateWarmHubClientOptions(options) {
|
|
|
9264
9298
|
throw new TypeError(`Unknown WarmHubClient option "${key}"${hint}`);
|
|
9265
9299
|
}
|
|
9266
9300
|
}
|
|
9267
|
-
var SDK_VERSION = "0.
|
|
9301
|
+
var SDK_VERSION = "0.61.0" ;
|
|
9268
9302
|
var DEFAULT_API_URL = "https://api.warmhub.ai";
|
|
9269
9303
|
var UNBATCHED_TRPC_PATHS = /* @__PURE__ */ new Set([
|
|
9270
9304
|
"repo.shapeInstanceCounts",
|
|
@@ -9599,6 +9633,8 @@ var WarmHubClient = class _WarmHubClient {
|
|
|
9599
9633
|
apiUrl;
|
|
9600
9634
|
fetchImpl;
|
|
9601
9635
|
accessToken;
|
|
9636
|
+
/** Resolved `X-WarmHub-Client` identity; defaults to this SDK's name/version. */
|
|
9637
|
+
clientIdentity;
|
|
9602
9638
|
functionLogMode;
|
|
9603
9639
|
getToken;
|
|
9604
9640
|
// Cached promise for diagnostics.assertCompatible(). Stored at instance
|
|
@@ -10515,21 +10551,6 @@ var WarmHubClient = class _WarmHubClient {
|
|
|
10515
10551
|
throw toWarmHubError(error);
|
|
10516
10552
|
}
|
|
10517
10553
|
},
|
|
10518
|
-
/**
|
|
10519
|
-
* Immediately and irreversibly purge a repository.
|
|
10520
|
-
*
|
|
10521
|
-
* This owner-only operation has no grace window and uses the same inbound-reference guard as `delete`.
|
|
10522
|
-
*/
|
|
10523
|
-
hardDelete: async (orgName, repoName) => {
|
|
10524
|
-
try {
|
|
10525
|
-
await this.trpc.repo.hardDelete.mutate({
|
|
10526
|
-
orgName,
|
|
10527
|
-
repoName
|
|
10528
|
-
});
|
|
10529
|
-
} catch (error) {
|
|
10530
|
-
throw toWarmHubError(error);
|
|
10531
|
-
}
|
|
10532
|
-
},
|
|
10533
10554
|
/**
|
|
10534
10555
|
* List repositories with dashboard-oriented per-repository metadata.
|
|
10535
10556
|
*
|
|
@@ -12162,6 +12183,10 @@ var WarmHubClient = class _WarmHubClient {
|
|
|
12162
12183
|
this.fetchImpl = options?.fetch;
|
|
12163
12184
|
this.accessToken = options?.accessToken ?? options?.auth?.getToken;
|
|
12164
12185
|
this.functionLogMode = options?.functionLogs ?? "off";
|
|
12186
|
+
this.clientIdentity = {
|
|
12187
|
+
name: options?.client?.name ?? WARMHUB_SDK_CLIENT_NAME,
|
|
12188
|
+
version: options?.client?.version ?? SDK_VERSION
|
|
12189
|
+
};
|
|
12165
12190
|
if (typeof this.accessToken === "function") {
|
|
12166
12191
|
const provider = this.accessToken;
|
|
12167
12192
|
this.getToken = async () => await provider();
|
|
@@ -12200,7 +12225,10 @@ var WarmHubClient = class _WarmHubClient {
|
|
|
12200
12225
|
return new _WarmHubClient({
|
|
12201
12226
|
apiUrl: this.apiUrl,
|
|
12202
12227
|
fetch: this.fetchImpl,
|
|
12203
|
-
accessToken
|
|
12228
|
+
accessToken,
|
|
12229
|
+
// Preserve the client identity so a token-scoped clone (e.g. a CLI clone)
|
|
12230
|
+
// keeps reporting `warmhub-cli/…` rather than reverting to the SDK default.
|
|
12231
|
+
client: this.clientIdentity
|
|
12204
12232
|
});
|
|
12205
12233
|
}
|
|
12206
12234
|
/**
|
|
@@ -12278,6 +12306,21 @@ var WarmHubClient = class _WarmHubClient {
|
|
|
12278
12306
|
closed
|
|
12279
12307
|
};
|
|
12280
12308
|
}
|
|
12309
|
+
/**
|
|
12310
|
+
* Stamp the `X-WarmHub-Client` self-identification header, unless the caller
|
|
12311
|
+
* already set it on the request (a per-request header wins over the default).
|
|
12312
|
+
*/
|
|
12313
|
+
applyClientHeader(headers) {
|
|
12314
|
+
if (!headers.has(CLIENT_HEADER)) {
|
|
12315
|
+
headers.set(
|
|
12316
|
+
CLIENT_HEADER,
|
|
12317
|
+
formatClientHeader(
|
|
12318
|
+
this.clientIdentity.name,
|
|
12319
|
+
this.clientIdentity.version
|
|
12320
|
+
)
|
|
12321
|
+
);
|
|
12322
|
+
}
|
|
12323
|
+
}
|
|
12281
12324
|
async fetchWithAuth(input, init) {
|
|
12282
12325
|
const fetchImpl = this.fetchImpl ?? globalThis.fetch;
|
|
12283
12326
|
const headers = new Headers(init?.headers);
|
|
@@ -12285,6 +12328,7 @@ var WarmHubClient = class _WarmHubClient {
|
|
|
12285
12328
|
if (token) {
|
|
12286
12329
|
headers.set("authorization", `Bearer ${token}`);
|
|
12287
12330
|
}
|
|
12331
|
+
this.applyClientHeader(headers);
|
|
12288
12332
|
const doFetch = async (reqInit) => {
|
|
12289
12333
|
try {
|
|
12290
12334
|
return await fetchImpl(input, reqInit);
|
|
@@ -12301,6 +12345,7 @@ var WarmHubClient = class _WarmHubClient {
|
|
|
12301
12345
|
if (freshToken && freshToken !== token) {
|
|
12302
12346
|
const retryHeaders = new Headers(init?.headers);
|
|
12303
12347
|
retryHeaders.set("authorization", `Bearer ${freshToken}`);
|
|
12348
|
+
this.applyClientHeader(retryHeaders);
|
|
12304
12349
|
return doFetch({ ...init, headers: retryHeaders });
|
|
12305
12350
|
}
|
|
12306
12351
|
}
|
|
@@ -12470,5 +12515,5 @@ re2js/build/index.js:
|
|
|
12470
12515
|
*/
|
|
12471
12516
|
|
|
12472
12517
|
export { AllStreamOperationsFailedError, CLI_INSTALL_REPO_HEADER, CLI_SIGNATURE_HEADER, CLI_TIMESTAMP_HEADER, CONTENT_FIELD_LIMIT_ERROR, CliCallVerificationError, DEFAULT_API_URL, DEFAULT_STREAM_CHUNK_SIZE, MAX_CONTENT_FIELD_BYTES, MAX_STREAM_APPEND_OPERATION_COUNT2 as MAX_STREAM_APPEND_OPERATION_COUNT, OperationBuilder, PartialStreamSubmissionError, SDK_VERSION, WarmHubClient, WarmHubError, connectionErrorMessage, contentFieldLimitError, countStreamAppendResultStatuses, isConnectionError, isRetryable, isWarmHubError, normalizeWref, resolveFunctionLogMode, sanitizeErrorMessage, sdkVersionIsBelowMinimum, streamAppendResultStatus, submitOperationsViaStream, toWarmHubError, validateAgainstShape, verifyCliCall };
|
|
12473
|
-
//# sourceMappingURL=chunk-
|
|
12474
|
-
//# sourceMappingURL=chunk-
|
|
12518
|
+
//# sourceMappingURL=chunk-QS6ABKPJ.js.map
|
|
12519
|
+
//# sourceMappingURL=chunk-QS6ABKPJ.js.map
|