@formancy/server-core 0.2.0 → 0.3.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/index.d.mts +141 -75
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +222 -189
- package/dist/index.mjs.map +1 -1
- package/package.json +4 -4
package/dist/index.mjs
CHANGED
|
@@ -3,9 +3,9 @@ import { hmac } from "@noble/hashes/hmac.js";
|
|
|
3
3
|
import { sha256 } from "@noble/hashes/sha2.js";
|
|
4
4
|
import { bytesToHex, utf8ToBytes } from "@noble/hashes/utils.js";
|
|
5
5
|
import { diffSchemas, modelDataPaths, schemaHash } from "@formancy/spec";
|
|
6
|
-
import {
|
|
7
|
-
import { createFormEngine, expressionProblems } from "@formancy/core";
|
|
6
|
+
import { createFormEngine, expressionProblems, unknownReferences } from "@formancy/core";
|
|
8
7
|
import { check } from "recheck";
|
|
8
|
+
import { validateSchema } from "@formancy/spec/validate";
|
|
9
9
|
//#region src/options-membership.ts
|
|
10
10
|
/**
|
|
11
11
|
* Every sourced answer in a submission, as `path → { source, value }`.
|
|
@@ -29,12 +29,15 @@ function sourcedAnswers(schema, value) {
|
|
|
29
29
|
continue;
|
|
30
30
|
}
|
|
31
31
|
if (field.optionsSource === void 0) continue;
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
32
|
+
const answers = Array.isArray(held) ? held : [held];
|
|
33
|
+
for (const answer of answers) {
|
|
34
|
+
if (typeof answer !== "string" || answer === "") continue;
|
|
35
|
+
found.push({
|
|
36
|
+
path: at,
|
|
37
|
+
source: field.optionsSource,
|
|
38
|
+
value: answer
|
|
39
|
+
});
|
|
40
|
+
}
|
|
38
41
|
}
|
|
39
42
|
};
|
|
40
43
|
walk(schema.model.fields, value, "");
|
|
@@ -623,187 +626,7 @@ function fileFieldPaths(schema) {
|
|
|
623
626
|
return modelDataPaths(schema.model).filter((path) => fileFieldAt(schema, path)?.type === "file");
|
|
624
627
|
}
|
|
625
628
|
//#endregion
|
|
626
|
-
//#region src/redos.ts
|
|
627
|
-
/** Every `pattern` in the model, with the data path of the field it sits on. */
|
|
628
|
-
function patternsIn(fields, prefix) {
|
|
629
|
-
const found = [];
|
|
630
|
-
for (const field of fields) {
|
|
631
|
-
const path = field.type === "page" ? prefix : field.type === "repeater" ? `${prefix}${field.key}[].` : field.type === "group" ? `${prefix}${field.key}.` : `${prefix}${field.key}`;
|
|
632
|
-
if (typeof field.pattern === "string" && field.pattern !== "") found.push({
|
|
633
|
-
path,
|
|
634
|
-
pattern: field.pattern
|
|
635
|
-
});
|
|
636
|
-
if (field.fields !== void 0) found.push(...patternsIn(field.fields, path));
|
|
637
|
-
}
|
|
638
|
-
return found;
|
|
639
|
-
}
|
|
640
|
-
/**
|
|
641
|
-
* The patterns in this schema that an attacker could exploit, or an empty array
|
|
642
|
-
* when there are none.
|
|
643
|
-
*
|
|
644
|
-
* A pattern `recheck` cannot decide about is ACCEPTED. Refusing on `unknown`
|
|
645
|
-
* would make publishing depend on an analysis timeout, which is a worse failure
|
|
646
|
-
* than the one being prevented — an author whose legitimate form stops
|
|
647
|
-
* publishing intermittently has no way to act on that.
|
|
648
|
-
*/
|
|
649
|
-
async function unsafePatterns(schema) {
|
|
650
|
-
const unsafe = [];
|
|
651
|
-
for (const { path, pattern } of patternsIn(schema.model.fields, "")) {
|
|
652
|
-
const diagnostics = await check(`^(?:${pattern})$`, "");
|
|
653
|
-
if (diagnostics.status !== "vulnerable") continue;
|
|
654
|
-
const complexity = diagnostics.complexity;
|
|
655
|
-
unsafe.push({
|
|
656
|
-
path,
|
|
657
|
-
pattern,
|
|
658
|
-
complexity: complexity === void 0 ? "unknown" : complexity.type === "polynomial" ? `polynomial degree ${String(complexity.degree)}` : complexity.type
|
|
659
|
-
});
|
|
660
|
-
}
|
|
661
|
-
return unsafe;
|
|
662
|
-
}
|
|
663
|
-
//#endregion
|
|
664
629
|
//#region src/use-cases.ts
|
|
665
|
-
/**
|
|
666
|
-
* Publish a schema as a form's next version.
|
|
667
|
-
*
|
|
668
|
-
* This is the save gate: structural validation, then the ENGINE's own compile
|
|
669
|
-
* — type-checked expressions, kind policies and the cycle graph — so a form
|
|
670
|
-
* that can loop or read a ghost field is refused here and never persisted.
|
|
671
|
-
* Republishing the identical document is idempotent, because "deploy again"
|
|
672
|
-
* must never manufacture a version.
|
|
673
|
-
*/
|
|
674
|
-
async function publishForm(deps, input) {
|
|
675
|
-
const validated = validateSchema(input.schema);
|
|
676
|
-
if (!validated.valid) return {
|
|
677
|
-
ok: false,
|
|
678
|
-
kind: "invalid_schema",
|
|
679
|
-
errors: validated.errors
|
|
680
|
-
};
|
|
681
|
-
const schema = validated.schema;
|
|
682
|
-
try {
|
|
683
|
-
createFormEngine({
|
|
684
|
-
schema,
|
|
685
|
-
capabilities: deps.capabilities
|
|
686
|
-
});
|
|
687
|
-
} catch (error) {
|
|
688
|
-
return {
|
|
689
|
-
ok: false,
|
|
690
|
-
kind: "invalid_logic",
|
|
691
|
-
message: error instanceof Error ? error.message : String(error)
|
|
692
|
-
};
|
|
693
|
-
}
|
|
694
|
-
const problems = expressionProblems(schema);
|
|
695
|
-
if (problems.length > 0) return {
|
|
696
|
-
ok: false,
|
|
697
|
-
kind: "invalid_logic",
|
|
698
|
-
message: problems.map((problem) => problem.message).join("\n")
|
|
699
|
-
};
|
|
700
|
-
const unsafe = await unsafePatterns(schema);
|
|
701
|
-
if (unsafe.length > 0) return {
|
|
702
|
-
ok: false,
|
|
703
|
-
kind: "unsafe_pattern",
|
|
704
|
-
patterns: unsafe
|
|
705
|
-
};
|
|
706
|
-
if (deps.optionsSources !== void 0) {
|
|
707
|
-
const unknown = sourceNamesIn(schema).filter((name) => deps.optionsSources?.[name] === void 0);
|
|
708
|
-
if (unknown.length > 0) return {
|
|
709
|
-
ok: false,
|
|
710
|
-
kind: "unknown_options_source",
|
|
711
|
-
sources: unknown
|
|
712
|
-
};
|
|
713
|
-
}
|
|
714
|
-
const hash = schemaHash(schema);
|
|
715
|
-
const existing = await deps.storage.getFormByPath(input.path);
|
|
716
|
-
if (existing !== void 0) {
|
|
717
|
-
const current = existing.currentVersionId === null ? void 0 : await deps.storage.getVersionById(existing.currentVersionId);
|
|
718
|
-
if (current !== void 0 && current.schemaHash === hash) return {
|
|
719
|
-
ok: true,
|
|
720
|
-
formId: existing.id,
|
|
721
|
-
versionId: current.id,
|
|
722
|
-
version: current.version,
|
|
723
|
-
schemaHash: hash
|
|
724
|
-
};
|
|
725
|
-
const version = await deps.storage.latestVersionNumber(existing.id) + 1;
|
|
726
|
-
const versionId = deps.newId();
|
|
727
|
-
await deps.storage.publishVersion({
|
|
728
|
-
version: {
|
|
729
|
-
id: versionId,
|
|
730
|
-
formId: existing.id,
|
|
731
|
-
version,
|
|
732
|
-
schema,
|
|
733
|
-
schemaHash: hash
|
|
734
|
-
},
|
|
735
|
-
audit: publishAudit(deps, input, {
|
|
736
|
-
path: input.path,
|
|
737
|
-
version,
|
|
738
|
-
hash,
|
|
739
|
-
sources: sourceNamesIn(schema)
|
|
740
|
-
})
|
|
741
|
-
});
|
|
742
|
-
return {
|
|
743
|
-
ok: true,
|
|
744
|
-
formId: existing.id,
|
|
745
|
-
versionId,
|
|
746
|
-
version,
|
|
747
|
-
schemaHash: hash
|
|
748
|
-
};
|
|
749
|
-
}
|
|
750
|
-
const formId = deps.newId();
|
|
751
|
-
const versionId = deps.newId();
|
|
752
|
-
await deps.storage.publishVersion({
|
|
753
|
-
form: {
|
|
754
|
-
id: formId,
|
|
755
|
-
path: input.path,
|
|
756
|
-
currentVersionId: null,
|
|
757
|
-
accessSubmit: "authenticated",
|
|
758
|
-
allowedOrigins: null
|
|
759
|
-
},
|
|
760
|
-
version: {
|
|
761
|
-
id: versionId,
|
|
762
|
-
formId,
|
|
763
|
-
version: 1,
|
|
764
|
-
schema,
|
|
765
|
-
schemaHash: hash
|
|
766
|
-
},
|
|
767
|
-
audit: publishAudit(deps, input, {
|
|
768
|
-
path: input.path,
|
|
769
|
-
version: 1,
|
|
770
|
-
hash,
|
|
771
|
-
sources: sourceNamesIn(schema)
|
|
772
|
-
})
|
|
773
|
-
});
|
|
774
|
-
return {
|
|
775
|
-
ok: true,
|
|
776
|
-
formId,
|
|
777
|
-
versionId,
|
|
778
|
-
version: 1,
|
|
779
|
-
schemaHash: hash
|
|
780
|
-
};
|
|
781
|
-
}
|
|
782
|
-
/**
|
|
783
|
-
* The audit row for a publish, built here so it can travel INSIDE the
|
|
784
|
-
* transaction rather than being appended after it.
|
|
785
|
-
*
|
|
786
|
-
* Before this it was written by the route once all three storage calls had
|
|
787
|
-
* returned, which meant a publish could half-apply and still be recorded as
|
|
788
|
-
* having happened. Now it rolls back with everything else.
|
|
789
|
-
*/
|
|
790
|
-
function publishAudit(deps, input, about) {
|
|
791
|
-
return {
|
|
792
|
-
id: deps.newId(),
|
|
793
|
-
at: deps.nowIso(),
|
|
794
|
-
action: "form.published",
|
|
795
|
-
subject: about.path,
|
|
796
|
-
...input.actor === void 0 ? {} : {
|
|
797
|
-
actorKind: input.actor.kind,
|
|
798
|
-
actorId: input.actor.id
|
|
799
|
-
},
|
|
800
|
-
detail: {
|
|
801
|
-
version: about.version,
|
|
802
|
-
schemaHash: about.hash,
|
|
803
|
-
...about.sources.length === 0 ? {} : { optionsSources: [...about.sources].sort().join(", ") }
|
|
804
|
-
}
|
|
805
|
-
};
|
|
806
|
-
}
|
|
807
630
|
async function resolveForm(deps, path) {
|
|
808
631
|
const form = await deps.storage.getFormByPath(path);
|
|
809
632
|
if (form === void 0 || form.currentVersionId === null) return void 0;
|
|
@@ -865,8 +688,11 @@ async function createSubmission(deps, input) {
|
|
|
865
688
|
const engine = createFormEngine({
|
|
866
689
|
schema: current.schema,
|
|
867
690
|
initialValue: input.data,
|
|
868
|
-
capabilities: deps.capabilities
|
|
691
|
+
capabilities: deps.capabilities,
|
|
692
|
+
mode: "server",
|
|
693
|
+
...deps.checks === void 0 ? {} : { checks: deps.checks }
|
|
869
694
|
});
|
|
695
|
+
await engine.settle();
|
|
870
696
|
const outcome = engine.submit();
|
|
871
697
|
if (!outcome.ok) return {
|
|
872
698
|
ok: false,
|
|
@@ -1224,6 +1050,44 @@ async function listVersions(deps, path) {
|
|
|
1224
1050
|
}));
|
|
1225
1051
|
}
|
|
1226
1052
|
//#endregion
|
|
1053
|
+
//#region src/redos.ts
|
|
1054
|
+
/** Every `pattern` in the model, with the data path of the field it sits on. */
|
|
1055
|
+
function patternsIn(fields, prefix) {
|
|
1056
|
+
const found = [];
|
|
1057
|
+
for (const field of fields) {
|
|
1058
|
+
const path = field.type === "page" ? prefix : field.type === "repeater" ? `${prefix}${field.key}[].` : field.type === "group" ? `${prefix}${field.key}.` : `${prefix}${field.key}`;
|
|
1059
|
+
if (typeof field.pattern === "string" && field.pattern !== "") found.push({
|
|
1060
|
+
path,
|
|
1061
|
+
pattern: field.pattern
|
|
1062
|
+
});
|
|
1063
|
+
if (field.fields !== void 0) found.push(...patternsIn(field.fields, path));
|
|
1064
|
+
}
|
|
1065
|
+
return found;
|
|
1066
|
+
}
|
|
1067
|
+
/**
|
|
1068
|
+
* The patterns in this schema that an attacker could exploit, or an empty array
|
|
1069
|
+
* when there are none.
|
|
1070
|
+
*
|
|
1071
|
+
* A pattern `recheck` cannot decide about is ACCEPTED. Refusing on `unknown`
|
|
1072
|
+
* would make publishing depend on an analysis timeout, which is a worse failure
|
|
1073
|
+
* than the one being prevented — an author whose legitimate form stops
|
|
1074
|
+
* publishing intermittently has no way to act on that.
|
|
1075
|
+
*/
|
|
1076
|
+
async function unsafePatterns(schema) {
|
|
1077
|
+
const unsafe = [];
|
|
1078
|
+
for (const { path, pattern } of patternsIn(schema.model.fields, "")) {
|
|
1079
|
+
const diagnostics = await check(`^(?:${pattern})$`, "");
|
|
1080
|
+
if (diagnostics.status !== "vulnerable") continue;
|
|
1081
|
+
const complexity = diagnostics.complexity;
|
|
1082
|
+
unsafe.push({
|
|
1083
|
+
path,
|
|
1084
|
+
pattern,
|
|
1085
|
+
complexity: complexity === void 0 ? "unknown" : complexity.type === "polynomial" ? `polynomial degree ${String(complexity.degree)}` : complexity.type
|
|
1086
|
+
});
|
|
1087
|
+
}
|
|
1088
|
+
return unsafe;
|
|
1089
|
+
}
|
|
1090
|
+
//#endregion
|
|
1227
1091
|
//#region src/testing/memory-storage.ts
|
|
1228
1092
|
/**
|
|
1229
1093
|
* The in-memory Storage — the second implementation that keeps the port
|
|
@@ -1543,6 +1407,175 @@ function isIpLiteral(host) {
|
|
|
1543
1407
|
return bare.includes(":");
|
|
1544
1408
|
}
|
|
1545
1409
|
//#endregion
|
|
1410
|
+
//#region src/publishing.ts
|
|
1411
|
+
/**
|
|
1412
|
+
* Publish a schema as a form's next version.
|
|
1413
|
+
*
|
|
1414
|
+
* This is the save gate: structural validation, then the ENGINE's own compile
|
|
1415
|
+
* — type-checked expressions, kind policies and the cycle graph — so a form
|
|
1416
|
+
* that can loop or read a ghost field is refused here and never persisted.
|
|
1417
|
+
* Republishing the identical document is idempotent, because "deploy again"
|
|
1418
|
+
* must never manufacture a version.
|
|
1419
|
+
*/
|
|
1420
|
+
async function publishForm(deps, input) {
|
|
1421
|
+
const validated = validateSchema(input.schema);
|
|
1422
|
+
if (!validated.valid) return {
|
|
1423
|
+
ok: false,
|
|
1424
|
+
kind: "invalid_schema",
|
|
1425
|
+
errors: validated.errors
|
|
1426
|
+
};
|
|
1427
|
+
const schema = validated.schema;
|
|
1428
|
+
try {
|
|
1429
|
+
createFormEngine({
|
|
1430
|
+
schema,
|
|
1431
|
+
capabilities: deps.capabilities
|
|
1432
|
+
});
|
|
1433
|
+
} catch (error) {
|
|
1434
|
+
return {
|
|
1435
|
+
ok: false,
|
|
1436
|
+
kind: "invalid_logic",
|
|
1437
|
+
message: error instanceof Error ? error.message : String(error)
|
|
1438
|
+
};
|
|
1439
|
+
}
|
|
1440
|
+
const problems = expressionProblems(schema);
|
|
1441
|
+
if (problems.length > 0) return {
|
|
1442
|
+
ok: false,
|
|
1443
|
+
kind: "invalid_logic",
|
|
1444
|
+
message: problems.map((problem) => problem.message).join("\n")
|
|
1445
|
+
};
|
|
1446
|
+
const warnings = unknownReferences(schema).map((found) => found.message);
|
|
1447
|
+
const unsafe = await unsafePatterns(schema);
|
|
1448
|
+
if (unsafe.length > 0) return {
|
|
1449
|
+
ok: false,
|
|
1450
|
+
kind: "unsafe_pattern",
|
|
1451
|
+
patterns: unsafe
|
|
1452
|
+
};
|
|
1453
|
+
if (deps.optionsSources !== void 0) {
|
|
1454
|
+
const unknown = sourceNamesIn(schema).filter((name) => deps.optionsSources?.[name] === void 0);
|
|
1455
|
+
if (unknown.length > 0) return {
|
|
1456
|
+
ok: false,
|
|
1457
|
+
kind: "unknown_options_source",
|
|
1458
|
+
sources: unknown
|
|
1459
|
+
};
|
|
1460
|
+
}
|
|
1461
|
+
const hash = schemaHash(schema);
|
|
1462
|
+
const existing = await deps.storage.getFormByPath(input.path);
|
|
1463
|
+
if (existing !== void 0) {
|
|
1464
|
+
const current = existing.currentVersionId === null ? void 0 : await deps.storage.getVersionById(existing.currentVersionId);
|
|
1465
|
+
if (current !== void 0 && current.schemaHash === hash) return {
|
|
1466
|
+
ok: true,
|
|
1467
|
+
formId: existing.id,
|
|
1468
|
+
versionId: current.id,
|
|
1469
|
+
version: current.version,
|
|
1470
|
+
schemaHash: hash,
|
|
1471
|
+
warnings
|
|
1472
|
+
};
|
|
1473
|
+
if (input.basedOnSchemaHash !== void 0 && input.basedOnSchemaHash !== current?.schemaHash) return {
|
|
1474
|
+
ok: false,
|
|
1475
|
+
kind: "version_changed",
|
|
1476
|
+
...current === void 0 ? {} : { current: {
|
|
1477
|
+
formId: existing.id,
|
|
1478
|
+
versionId: current.id,
|
|
1479
|
+
version: current.version,
|
|
1480
|
+
schema: current.schema,
|
|
1481
|
+
schemaHash: current.schemaHash
|
|
1482
|
+
} }
|
|
1483
|
+
};
|
|
1484
|
+
const already = (await deps.storage.listVersionsByForm(existing.id)).find((candidate) => candidate.schemaHash === hash);
|
|
1485
|
+
if (already !== void 0) return {
|
|
1486
|
+
ok: false,
|
|
1487
|
+
kind: "already_published",
|
|
1488
|
+
version: already.version
|
|
1489
|
+
};
|
|
1490
|
+
const version = await deps.storage.latestVersionNumber(existing.id) + 1;
|
|
1491
|
+
const versionId = deps.newId();
|
|
1492
|
+
await deps.storage.publishVersion({
|
|
1493
|
+
version: {
|
|
1494
|
+
id: versionId,
|
|
1495
|
+
formId: existing.id,
|
|
1496
|
+
version,
|
|
1497
|
+
schema,
|
|
1498
|
+
schemaHash: hash
|
|
1499
|
+
},
|
|
1500
|
+
audit: publishAudit(deps, input, {
|
|
1501
|
+
path: input.path,
|
|
1502
|
+
version,
|
|
1503
|
+
hash,
|
|
1504
|
+
sources: sourceNamesIn(schema)
|
|
1505
|
+
})
|
|
1506
|
+
});
|
|
1507
|
+
return {
|
|
1508
|
+
ok: true,
|
|
1509
|
+
formId: existing.id,
|
|
1510
|
+
versionId,
|
|
1511
|
+
version,
|
|
1512
|
+
schemaHash: hash,
|
|
1513
|
+
warnings
|
|
1514
|
+
};
|
|
1515
|
+
}
|
|
1516
|
+
if (input.basedOnSchemaHash !== void 0) return {
|
|
1517
|
+
ok: false,
|
|
1518
|
+
kind: "version_changed"
|
|
1519
|
+
};
|
|
1520
|
+
const formId = deps.newId();
|
|
1521
|
+
const versionId = deps.newId();
|
|
1522
|
+
await deps.storage.publishVersion({
|
|
1523
|
+
form: {
|
|
1524
|
+
id: formId,
|
|
1525
|
+
path: input.path,
|
|
1526
|
+
currentVersionId: null,
|
|
1527
|
+
accessSubmit: "authenticated",
|
|
1528
|
+
allowedOrigins: null
|
|
1529
|
+
},
|
|
1530
|
+
version: {
|
|
1531
|
+
id: versionId,
|
|
1532
|
+
formId,
|
|
1533
|
+
version: 1,
|
|
1534
|
+
schema,
|
|
1535
|
+
schemaHash: hash
|
|
1536
|
+
},
|
|
1537
|
+
audit: publishAudit(deps, input, {
|
|
1538
|
+
path: input.path,
|
|
1539
|
+
version: 1,
|
|
1540
|
+
hash,
|
|
1541
|
+
sources: sourceNamesIn(schema)
|
|
1542
|
+
})
|
|
1543
|
+
});
|
|
1544
|
+
return {
|
|
1545
|
+
ok: true,
|
|
1546
|
+
formId,
|
|
1547
|
+
versionId,
|
|
1548
|
+
version: 1,
|
|
1549
|
+
schemaHash: hash,
|
|
1550
|
+
warnings
|
|
1551
|
+
};
|
|
1552
|
+
}
|
|
1553
|
+
/**
|
|
1554
|
+
* The audit row for a publish, built here so it can travel INSIDE the
|
|
1555
|
+
* transaction rather than being appended after it.
|
|
1556
|
+
*
|
|
1557
|
+
* Before this it was written by the route once all three storage calls had
|
|
1558
|
+
* returned, which meant a publish could half-apply and still be recorded as
|
|
1559
|
+
* having happened. Now it rolls back with everything else.
|
|
1560
|
+
*/
|
|
1561
|
+
function publishAudit(deps, input, about) {
|
|
1562
|
+
return {
|
|
1563
|
+
id: deps.newId(),
|
|
1564
|
+
at: deps.nowIso(),
|
|
1565
|
+
action: "form.published",
|
|
1566
|
+
subject: about.path,
|
|
1567
|
+
...input.actor === void 0 ? {} : {
|
|
1568
|
+
actorKind: input.actor.kind,
|
|
1569
|
+
actorId: input.actor.id
|
|
1570
|
+
},
|
|
1571
|
+
detail: {
|
|
1572
|
+
version: about.version,
|
|
1573
|
+
schemaHash: about.hash,
|
|
1574
|
+
...about.sources.length === 0 ? {} : { optionsSources: [...about.sources].sort().join(", ") }
|
|
1575
|
+
}
|
|
1576
|
+
};
|
|
1577
|
+
}
|
|
1578
|
+
//#endregion
|
|
1546
1579
|
export { AUDIT_ACTIONS, BREAKER_COOLDOWN_MS, BREAKER_THRESHOLD, CHALLENGE_TTL_SECONDS, DEFAULT_MAX_NUMBER, EVENT_ID_HEADER, MAX_ATTEMPTS, SIGNATURE_HEADER, afterAttempt, afterWebhookAttempt, auditedBy, authenticateApiKey, authenticateLocal, breakerState, can, checkMembership, collectAbandonedFiles, createApiKey, createLocalUser, createMemoryStorage, createSubmission, decodeSolution, deliveryHeaders, drainOutbox, encodeSolution, exportCsv, fileFieldPaths, filesToClaim, hashOf, healthOf, isIpLiteral, isPrivateAddress, listForms, listSubmissions, listVersions, mayAttempt, mintChallenge, offerUpload, publishForm, replayDelivery, resolveForm, resumeDraft, retryDelayMs, saveDraft, setFormAccess, signBody, signedPayload, solveChallenge, sourceNamesIn, sourcedAnswers, startDraft, unsafePatterns, verifySignature, verifySolution };
|
|
1547
1580
|
|
|
1548
1581
|
//# sourceMappingURL=index.mjs.map
|