@formancy/server-core 0.2.0 → 0.4.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 +276 -146
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +255 -190
- package/dist/index.mjs.map +1 -1
- package/package.json +4 -4
package/dist/index.d.mts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { CHALLENGE_TTL_SECONDS, Challenge, DEFAULT_MAX_NUMBER, Solution, VerifyOutcome, decodeSolution, encodeSolution, hashOf, mintChallenge, solveChallenge, verifySolution } from "@formancy/challenge";
|
|
2
2
|
import { Change, FormSchema } from "@formancy/spec";
|
|
3
|
+
import { CapabilitySource, Check } from "@formancy/core";
|
|
3
4
|
import { SchemaError } from "@formancy/spec/validate";
|
|
4
|
-
import { CapabilitySource } from "@formancy/core";
|
|
5
5
|
//#region src/options-membership.d.ts
|
|
6
6
|
/**
|
|
7
7
|
* Checking that a sourced answer is one the deployment's own source offers.
|
|
@@ -606,53 +606,172 @@ interface WebhookHealth {
|
|
|
606
606
|
}
|
|
607
607
|
export declare function healthOf(webhook: WebhookRecord, now: Date): WebhookHealth;
|
|
608
608
|
//#endregion
|
|
609
|
-
//#region src/
|
|
609
|
+
//#region src/uploads.d.ts
|
|
610
610
|
/**
|
|
611
|
-
*
|
|
611
|
+
* The file lifecycle: offered, stored, claimed, collected.
|
|
612
612
|
*
|
|
613
|
-
*
|
|
614
|
-
*
|
|
615
|
-
*
|
|
616
|
-
*
|
|
613
|
+
* **Bytes are not the submission's problem.** A submission stores what each
|
|
614
|
+
* file is and where it went, never the bytes themselves, so a submission read
|
|
615
|
+
* back years later is small, self-describing, and still says what was attached
|
|
616
|
+
* even if the object store has since been emptied.
|
|
617
617
|
*
|
|
618
|
-
*
|
|
619
|
-
*
|
|
620
|
-
*
|
|
618
|
+
* **The claim happens inside the submission's transaction.** A submission
|
|
619
|
+
* exists if and only if the files it names are claimed against it. Anything
|
|
620
|
+
* looser gives one of the two failures nobody can debug: a rolled-back
|
|
621
|
+
* submission leaving files claimed against nothing, or an accepted submission
|
|
622
|
+
* referencing files the collector is about to delete.
|
|
621
623
|
*
|
|
622
|
-
*
|
|
623
|
-
*
|
|
624
|
-
*
|
|
624
|
+
* **Nothing here touches a disk.** This package has no filesystem and no
|
|
625
|
+
* object store; it decides what is rubbish and the host deletes it. That is
|
|
626
|
+
* the same split every other port here makes, and it is why the whole
|
|
627
|
+
* lifecycle is testable without either.
|
|
625
628
|
*/
|
|
626
|
-
interface
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
629
|
+
interface OfferDeps {
|
|
630
|
+
storage: Storage;
|
|
631
|
+
now: () => Date;
|
|
632
|
+
newId: () => string;
|
|
633
|
+
}
|
|
634
|
+
interface OfferInput {
|
|
635
|
+
schema: FormSchema;
|
|
636
|
+
/** The form, for the access check — the same one a submission passes. */
|
|
637
|
+
form: FormRecord;
|
|
638
|
+
actor: 'anonymous' | 'authenticated';
|
|
639
|
+
origin: string | undefined;
|
|
640
|
+
/** The field the file is for, as a data path: `evidence`, `people[].passport`. */
|
|
641
|
+
field: string;
|
|
642
|
+
name: string;
|
|
643
|
+
size: number;
|
|
644
|
+
contentType: string;
|
|
632
645
|
}
|
|
646
|
+
type OfferOutcome = {
|
|
647
|
+
ok: true;
|
|
648
|
+
file: FileRecord;
|
|
649
|
+
} | {
|
|
650
|
+
ok: false;
|
|
651
|
+
reason: 'not_allowed' | 'unknown_field' | 'not_a_file_field' | 'too_large' | 'not_accepted';
|
|
652
|
+
};
|
|
633
653
|
/**
|
|
634
|
-
*
|
|
635
|
-
* when there are none.
|
|
654
|
+
* Agree where a file will go, and refuse it before a byte is sent.
|
|
636
655
|
*
|
|
637
|
-
*
|
|
638
|
-
*
|
|
639
|
-
*
|
|
640
|
-
*
|
|
656
|
+
* Checking here rather than after the upload is the difference between
|
|
657
|
+
* refusing a 2 GB file and receiving one first. The browser's own `accept` and
|
|
658
|
+
* size limits are a convenience for the person filling the form in; these are
|
|
659
|
+
* the rule, because somebody posting to the endpoint directly has neither.
|
|
641
660
|
*/
|
|
642
|
-
export declare function
|
|
661
|
+
export declare function offerUpload(deps: OfferDeps, input: OfferInput): Promise<OfferOutcome>;
|
|
662
|
+
/** What a deployment's scanner says about an upload's bytes. */
|
|
663
|
+
type ScanVerdict = {
|
|
664
|
+
clean: true;
|
|
665
|
+
} | {
|
|
666
|
+
clean: false;
|
|
667
|
+
finding: string;
|
|
668
|
+
};
|
|
669
|
+
/**
|
|
670
|
+
* A deployment's virus scanner, asked about an upload's bytes before they are kept.
|
|
671
|
+
*
|
|
672
|
+
* A port because the deployment supplies it and there are two: the ClamAV adapter in
|
|
673
|
+
* `@formancy/server`, and the doubles that test this decision. **Throwing means it could
|
|
674
|
+
* not be asked** — a connection refused, a timeout — and never that the file is clean.
|
|
675
|
+
*/
|
|
676
|
+
interface Scanner {
|
|
677
|
+
scan(file: {
|
|
678
|
+
name: string;
|
|
679
|
+
contentType: string;
|
|
680
|
+
size: number;
|
|
681
|
+
}, bytes: Uint8Array): Promise<ScanVerdict>;
|
|
682
|
+
}
|
|
683
|
+
type ScreenOutcome = {
|
|
684
|
+
ok: true;
|
|
685
|
+
} | {
|
|
686
|
+
ok: false;
|
|
687
|
+
reason: 'refused';
|
|
688
|
+
finding: string;
|
|
689
|
+
} |
|
|
690
|
+
/** `cause` is for the operator's log, never for the client: it can name an address. */
|
|
691
|
+
{
|
|
692
|
+
ok: false;
|
|
693
|
+
reason: 'scanner_unavailable';
|
|
694
|
+
cause: string;
|
|
695
|
+
};
|
|
696
|
+
/**
|
|
697
|
+
* Whether an upload's bytes may be kept.
|
|
698
|
+
*
|
|
699
|
+
* **Fail closed.** A scanner that cannot be asked refuses the file, the same as one that
|
|
700
|
+
* found something: a deployment that configured a scanner did so to keep unscanned files
|
|
701
|
+
* out, and its being down is not a reason to let one in. The person is told to try again,
|
|
702
|
+
* and the field offers exactly that. Without a scanner every upload is kept, as it was
|
|
703
|
+
* before there was one — a supported state, said in the documentation rather than here
|
|
704
|
+
* ([0131](../../../docs/decisions/0131-an-upload-is-scanned-before-it-is-kept.md)).
|
|
705
|
+
*/
|
|
706
|
+
export declare function screenUpload(scanner: Scanner | undefined, file: {
|
|
707
|
+
name: string;
|
|
708
|
+
contentType: string;
|
|
709
|
+
size: number;
|
|
710
|
+
}, bytes: Uint8Array): Promise<ScreenOutcome>;
|
|
711
|
+
type ClaimOutcome = {
|
|
712
|
+
ok: true;
|
|
713
|
+
ids: readonly string[];
|
|
714
|
+
} | {
|
|
715
|
+
ok: false;
|
|
716
|
+
unknown: readonly string[];
|
|
717
|
+
};
|
|
718
|
+
/**
|
|
719
|
+
* Which files a submission may claim, checked but not yet claimed.
|
|
720
|
+
*
|
|
721
|
+
* The claiming itself is `insertSubmission`'s job, in the same call that
|
|
722
|
+
* stores the submission, so one COMMIT decides both — the same reasoning
|
|
723
|
+
* that put the webhook queue there. A submission exists if and only if the
|
|
724
|
+
* files it names belong to it. Claiming separately gives the two failures
|
|
725
|
+
* nobody can debug: a rolled-back submission leaving files claimed against
|
|
726
|
+
* nothing, or an accepted submission referencing bytes the collector is about
|
|
727
|
+
* to delete.
|
|
728
|
+
*
|
|
729
|
+
* Refuses rather than skips. A submission naming a file that does not exist,
|
|
730
|
+
* belongs to another form, or is already claimed is not a submission to fix up
|
|
731
|
+
* quietly: the first is a bug or somebody guessing ids; the second is how an
|
|
732
|
+
* id from a form you may submit to would let you attach a file from one you
|
|
733
|
+
* may not; the third would make deleting a submission either leak bytes or
|
|
734
|
+
* break a different one.
|
|
735
|
+
*/
|
|
736
|
+
export declare function filesToClaim(storage: Storage, input: {
|
|
737
|
+
schema: FormSchema;
|
|
738
|
+
formId: string;
|
|
739
|
+
data: unknown;
|
|
740
|
+
}): Promise<ClaimOutcome>;
|
|
741
|
+
/**
|
|
742
|
+
* Files nobody claimed, old enough that nobody is going to.
|
|
743
|
+
*
|
|
744
|
+
* Returns the rows rather than deleting anything: the host removes the bytes
|
|
745
|
+
* and then the rows, in that order, because a row without bytes is a broken
|
|
746
|
+
* reference and bytes without a row are invisible rubbish. Of the two, the
|
|
747
|
+
* second is the one you never find out about.
|
|
748
|
+
*/
|
|
749
|
+
export declare function collectAbandonedFiles(storage: Storage, now: Date, afterHours: number): Promise<FileRecord[]>;
|
|
750
|
+
/** Re-exported so a host can see which paths a schema's file fields live at. */
|
|
751
|
+
export declare function fileFieldPaths(schema: FormSchema): string[];
|
|
643
752
|
//#endregion
|
|
644
|
-
//#region src/
|
|
753
|
+
//#region src/deps.d.ts
|
|
645
754
|
/**
|
|
646
|
-
*
|
|
755
|
+
* What a deployment supplies to the use-cases: storage, identity, time, and the
|
|
756
|
+
* answers a document may only name — checks, lists and a scanner.
|
|
647
757
|
*
|
|
648
|
-
*
|
|
649
|
-
*
|
|
650
|
-
*
|
|
758
|
+
* Its own file because it is the one contract every use-case family shares, and
|
|
759
|
+
* `use-cases.ts` is where the families used to live; it moved when that file's size
|
|
760
|
+
* budget refused the scanner.
|
|
651
761
|
*/
|
|
652
762
|
interface ServerDeps {
|
|
653
763
|
storage: Storage;
|
|
654
764
|
newId(): string;
|
|
655
765
|
nowIso(): string;
|
|
766
|
+
/**
|
|
767
|
+
* Validators this deployment answers, by the name a `check` rule gives.
|
|
768
|
+
*
|
|
769
|
+
* The same shape `optionsSources` takes, and the same rule: the document names
|
|
770
|
+
* a check, the deployment says how to answer it, and nothing in the engine
|
|
771
|
+
* fetches anything. A check a document names and a deployment has not supplied
|
|
772
|
+
* fails the field closed.
|
|
773
|
+
*/
|
|
774
|
+
checks?: Record<string, Check>;
|
|
656
775
|
/** The clock/randomness the ENGINE sees during replay. */
|
|
657
776
|
capabilities: CapabilitySource;
|
|
658
777
|
/**
|
|
@@ -680,46 +799,21 @@ interface ServerDeps {
|
|
|
680
799
|
* legitimate configuration, and the reason the guarantee is written down.
|
|
681
800
|
*/
|
|
682
801
|
optionsSources?: ServerOptionsSources;
|
|
802
|
+
/**
|
|
803
|
+
* The deployment's virus scanner, asked about every upload's bytes before they are
|
|
804
|
+
* kept. Absent means none is asked — a supported state, and the documented one.
|
|
805
|
+
*/
|
|
806
|
+
scanner?: Scanner;
|
|
683
807
|
}
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
formId: string;
|
|
687
|
-
versionId: string;
|
|
688
|
-
version: number;
|
|
689
|
-
schemaHash: string;
|
|
690
|
-
} | {
|
|
691
|
-
ok: false;
|
|
692
|
-
kind: 'invalid_schema';
|
|
693
|
-
errors?: SchemaError[];
|
|
694
|
-
} | {
|
|
695
|
-
ok: false;
|
|
696
|
-
kind: 'invalid_logic';
|
|
697
|
-
message: string;
|
|
698
|
-
} | {
|
|
699
|
-
ok: false;
|
|
700
|
-
kind: 'unsafe_pattern';
|
|
701
|
-
patterns: UnsafePattern[];
|
|
702
|
-
} |
|
|
703
|
-
/** The document names a list this deployment has never heard of. */
|
|
704
|
-
{
|
|
705
|
-
ok: false;
|
|
706
|
-
kind: 'unknown_options_source';
|
|
707
|
-
sources: string[];
|
|
708
|
-
};
|
|
808
|
+
//#endregion
|
|
809
|
+
//#region src/use-cases.d.ts
|
|
709
810
|
/**
|
|
710
|
-
*
|
|
811
|
+
* The backend's use-cases, framework-free.
|
|
711
812
|
*
|
|
712
|
-
*
|
|
713
|
-
*
|
|
714
|
-
*
|
|
715
|
-
* Republishing the identical document is idempotent, because "deploy again"
|
|
716
|
-
* must never manufacture a version.
|
|
813
|
+
* Identity and time are injected like every other capability: the server pins
|
|
814
|
+
* one clock per request, so a submission can be replayed later and produce the
|
|
815
|
+
* same computed values byte for byte. Nothing here reads Date.now or crypto.
|
|
717
816
|
*/
|
|
718
|
-
export declare function publishForm(deps: ServerDeps, input: {
|
|
719
|
-
path: string;
|
|
720
|
-
schema: unknown;
|
|
721
|
-
actor?: Actor;
|
|
722
|
-
}): Promise<PublishOutcome>;
|
|
723
817
|
interface ResolvedForm {
|
|
724
818
|
formId: string;
|
|
725
819
|
versionId: string;
|
|
@@ -889,6 +983,41 @@ interface ListedVersion {
|
|
|
889
983
|
* view rendering fifty full schemas would be its own denial of service. */
|
|
890
984
|
export declare function listVersions(deps: ServerDeps, path: string): Promise<ListedVersion[] | undefined>;
|
|
891
985
|
//#endregion
|
|
986
|
+
//#region src/redos.d.ts
|
|
987
|
+
/**
|
|
988
|
+
* Refuse a form whose `pattern` values can be made to backtrack.
|
|
989
|
+
*
|
|
990
|
+
* A form author supplies these, and the server runs them against whatever a
|
|
991
|
+
* submitter typed. That makes a regular expression an author-side denial of
|
|
992
|
+
* service vector: one bad pattern, published once, and every submission after
|
|
993
|
+
* it can be turned into unbounded CPU by anyone who can reach the form.
|
|
994
|
+
*
|
|
995
|
+
* Checked at PUBLISH time rather than at match time, because there is no way
|
|
996
|
+
* to time out a JavaScript regular expression once it has started. The only
|
|
997
|
+
* moment the cost can be refused is before the pattern is stored.
|
|
998
|
+
*
|
|
999
|
+
* The check is static — `recheck` analyses the expression rather than running
|
|
1000
|
+
* it — so it costs nothing per submission, and it found a polynomial case in
|
|
1001
|
+
* formancy's own built-in email format before it was ever pointed at a user's.
|
|
1002
|
+
*/
|
|
1003
|
+
interface UnsafePattern {
|
|
1004
|
+
/** Data path of the field carrying it, e.g. `contacts[].code`. */
|
|
1005
|
+
path: string;
|
|
1006
|
+
pattern: string;
|
|
1007
|
+
/** `exponential` or `polynomial`, with the degree where there is one. */
|
|
1008
|
+
complexity: string;
|
|
1009
|
+
}
|
|
1010
|
+
/**
|
|
1011
|
+
* The patterns in this schema that an attacker could exploit, or an empty array
|
|
1012
|
+
* when there are none.
|
|
1013
|
+
*
|
|
1014
|
+
* A pattern `recheck` cannot decide about is ACCEPTED. Refusing on `unknown`
|
|
1015
|
+
* would make publishing depend on an analysis timeout, which is a worse failure
|
|
1016
|
+
* than the one being prevented — an author whose legitimate form stops
|
|
1017
|
+
* publishing intermittently has no way to act on that.
|
|
1018
|
+
*/
|
|
1019
|
+
export declare function unsafePatterns(schema: FormSchema): Promise<UnsafePattern[]>;
|
|
1020
|
+
//#endregion
|
|
892
1021
|
//#region src/testing/memory-storage.d.ts
|
|
893
1022
|
/**
|
|
894
1023
|
* The in-memory Storage — the second implementation that keeps the port
|
|
@@ -998,100 +1127,101 @@ export declare function retryDelayMs(attempt: number, random: () => number): num
|
|
|
998
1127
|
/** Eight attempts reach roughly 24 hours with this schedule. */
|
|
999
1128
|
export declare const MAX_ATTEMPTS = 8;
|
|
1000
1129
|
//#endregion
|
|
1001
|
-
//#region src/
|
|
1130
|
+
//#region src/publishing.d.ts
|
|
1002
1131
|
/**
|
|
1003
|
-
*
|
|
1132
|
+
* Publishing a form: the save gate, and who may overwrite whom.
|
|
1004
1133
|
*
|
|
1005
|
-
*
|
|
1006
|
-
*
|
|
1007
|
-
*
|
|
1008
|
-
* even if the object store has since been emptied.
|
|
1009
|
-
*
|
|
1010
|
-
* **The claim happens inside the submission's transaction.** A submission
|
|
1011
|
-
* exists if and only if the files it names are claimed against it. Anything
|
|
1012
|
-
* looser gives one of the two failures nobody can debug: a rolled-back
|
|
1013
|
-
* submission leaving files claimed against nothing, or an accepted submission
|
|
1014
|
-
* referencing files the collector is about to delete.
|
|
1015
|
-
*
|
|
1016
|
-
* **Nothing here touches a disk.** This package has no filesystem and no
|
|
1017
|
-
* object store; it decides what is rubbish and the host deletes it. That is
|
|
1018
|
-
* the same split every other port here makes, and it is why the whole
|
|
1019
|
-
* lifecycle is testable without either.
|
|
1134
|
+
* Its own file because `use-cases.ts` had become the place every family went,
|
|
1135
|
+
* and the size budget refused the next thing added to it — the entry already
|
|
1136
|
+
* said the seam was one use-case family per file, and this is the family.
|
|
1020
1137
|
*/
|
|
1021
|
-
|
|
1022
|
-
storage: Storage;
|
|
1023
|
-
now: () => Date;
|
|
1024
|
-
newId: () => string;
|
|
1025
|
-
}
|
|
1026
|
-
interface OfferInput {
|
|
1027
|
-
schema: FormSchema;
|
|
1028
|
-
/** The form, for the access check — the same one a submission passes. */
|
|
1029
|
-
form: FormRecord;
|
|
1030
|
-
actor: 'anonymous' | 'authenticated';
|
|
1031
|
-
origin: string | undefined;
|
|
1032
|
-
/** The field the file is for, as a data path: `evidence`, `people[].passport`. */
|
|
1033
|
-
field: string;
|
|
1034
|
-
name: string;
|
|
1035
|
-
size: number;
|
|
1036
|
-
contentType: string;
|
|
1037
|
-
}
|
|
1038
|
-
type OfferOutcome = {
|
|
1138
|
+
type PublishOutcome = {
|
|
1039
1139
|
ok: true;
|
|
1040
|
-
|
|
1140
|
+
formId: string;
|
|
1141
|
+
versionId: string;
|
|
1142
|
+
version: number;
|
|
1143
|
+
schemaHash: string;
|
|
1144
|
+
/**
|
|
1145
|
+
* Things worth telling the publisher that are not grounds to refuse.
|
|
1146
|
+
*
|
|
1147
|
+
* Always present, empty when there is nothing to say, so a caller reads a
|
|
1148
|
+
* list rather than branching on whether there is one. Sentences rather
|
|
1149
|
+
* than codes: the only consumers are a person reading a CLI, an admin
|
|
1150
|
+
* screen or a log, and a code would need a catalogue nobody would write
|
|
1151
|
+
* ([0097](../../../docs/decisions/0097-a-publish-may-warn.md)).
|
|
1152
|
+
*/
|
|
1153
|
+
warnings: readonly string[];
|
|
1041
1154
|
} | {
|
|
1042
1155
|
ok: false;
|
|
1043
|
-
|
|
1044
|
-
|
|
1156
|
+
kind: 'invalid_schema';
|
|
1157
|
+
errors?: SchemaError[];
|
|
1158
|
+
} | {
|
|
1159
|
+
ok: false;
|
|
1160
|
+
kind: 'invalid_logic';
|
|
1161
|
+
message: string;
|
|
1162
|
+
} | {
|
|
1163
|
+
ok: false;
|
|
1164
|
+
kind: 'unsafe_pattern';
|
|
1165
|
+
patterns: UnsafePattern[];
|
|
1166
|
+
} |
|
|
1167
|
+
/** The document names a list this deployment has never heard of. */
|
|
1168
|
+
{
|
|
1169
|
+
ok: false;
|
|
1170
|
+
kind: 'unknown_options_source';
|
|
1171
|
+
sources: string[];
|
|
1172
|
+
} |
|
|
1045
1173
|
/**
|
|
1046
|
-
*
|
|
1174
|
+
* Somebody else published while this editor was editing.
|
|
1047
1175
|
*
|
|
1048
|
-
*
|
|
1049
|
-
*
|
|
1050
|
-
* size limits are a convenience for the person filling the form in; these are
|
|
1051
|
-
* the rule, because somebody posting to the endpoint directly has neither.
|
|
1176
|
+
* The same kind and the same body the submission path uses, deliberately: a
|
|
1177
|
+
* client that already knows how to handle one stale version handles both.
|
|
1052
1178
|
*/
|
|
1053
|
-
|
|
1054
|
-
type ClaimOutcome = {
|
|
1055
|
-
ok: true;
|
|
1056
|
-
ids: readonly string[];
|
|
1057
|
-
} | {
|
|
1179
|
+
{
|
|
1058
1180
|
ok: false;
|
|
1059
|
-
|
|
1060
|
-
|
|
1181
|
+
kind: 'version_changed';
|
|
1182
|
+
current?: ResolvedForm;
|
|
1183
|
+
} |
|
|
1061
1184
|
/**
|
|
1062
|
-
*
|
|
1185
|
+
* This exact document is already a version of this form, and not the current
|
|
1186
|
+
* one — somebody republishing an older version to undo a bad publish.
|
|
1063
1187
|
*
|
|
1064
|
-
*
|
|
1065
|
-
*
|
|
1066
|
-
*
|
|
1067
|
-
*
|
|
1068
|
-
*
|
|
1069
|
-
*
|
|
1070
|
-
* to delete.
|
|
1071
|
-
*
|
|
1072
|
-
* Refuses rather than skips. A submission naming a file that does not exist,
|
|
1073
|
-
* belongs to another form, or is already claimed is not a submission to fix up
|
|
1074
|
-
* quietly: the first is a bug or somebody guessing ids; the second is how an
|
|
1075
|
-
* id from a form you may submit to would let you attach a file from one you
|
|
1076
|
-
* may not; the third would make deleting a submission either leak bytes or
|
|
1077
|
-
* break a different one.
|
|
1188
|
+
* `UNIQUE (form_id, schema_hash)` is what makes republishing the CURRENT
|
|
1189
|
+
* document idempotent rather than a version factory, and it refuses this too:
|
|
1190
|
+
* without this branch the insert raised and the route answered **500**. A
|
|
1191
|
+
* published version is immutable and cannot be published twice
|
|
1192
|
+
* ([0025](../../../docs/decisions/0025-immutability-in-the-database.md)), so
|
|
1193
|
+
* the honest answer names the version it already is.
|
|
1078
1194
|
*/
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
}
|
|
1195
|
+
{
|
|
1196
|
+
ok: false;
|
|
1197
|
+
kind: 'already_published';
|
|
1198
|
+
version: number;
|
|
1199
|
+
};
|
|
1084
1200
|
/**
|
|
1085
|
-
*
|
|
1201
|
+
* Publish a schema as a form's next version.
|
|
1086
1202
|
*
|
|
1087
|
-
*
|
|
1088
|
-
*
|
|
1089
|
-
*
|
|
1090
|
-
*
|
|
1203
|
+
* This is the save gate: structural validation, then the ENGINE's own compile
|
|
1204
|
+
* — type-checked expressions, kind policies and the cycle graph — so a form
|
|
1205
|
+
* that can loop or read a ghost field is refused here and never persisted.
|
|
1206
|
+
* Republishing the identical document is idempotent, because "deploy again"
|
|
1207
|
+
* must never manufacture a version.
|
|
1091
1208
|
*/
|
|
1092
|
-
export declare function
|
|
1093
|
-
|
|
1094
|
-
|
|
1209
|
+
export declare function publishForm(deps: ServerDeps, input: {
|
|
1210
|
+
path: string;
|
|
1211
|
+
schema: unknown;
|
|
1212
|
+
actor?: Actor;
|
|
1213
|
+
/**
|
|
1214
|
+
* The hash of the version this edit started from, when there was one.
|
|
1215
|
+
*
|
|
1216
|
+
* **Optional, and that is the decision.** A script, the CLI and an agent
|
|
1217
|
+
* publish a document they composed rather than one they opened, and have
|
|
1218
|
+
* nothing to declare; requiring it would make every one of them read the
|
|
1219
|
+
* current version first to satisfy a rule about editors. What declares is
|
|
1220
|
+
* the thing that OPENED a version — the builder — and declaring is how it
|
|
1221
|
+
* asks to be told it has been overtaken.
|
|
1222
|
+
*/
|
|
1223
|
+
basedOnSchemaHash?: string;
|
|
1224
|
+
}): Promise<PublishOutcome>;
|
|
1095
1225
|
//#endregion
|
|
1096
|
-
export { type Action, type Actor, type AttemptOutcome, type AuditAction, type AuditDraft, type AuditEntry, type AuthDeps, type AuthOutcome, type BreakerState, CHALLENGE_TTL_SECONDS, type Challenge, type ClaimOutcome, DEFAULT_MAX_NUMBER, type DeliveryHeaders, type DeliveryRecord, type DraftMigration, type DraftRecord, type FileRecord, type FormRecord, type FormVersionRecord, type ListedForm, type ListedSubmission, type ListedVersion, type MembershipOutcome, type OfferDeps, type OfferInput, type OfferOutcome, type OutboxDeps, type PublishOutcome, type ReplayOutcome, type ResolvedForm, type ResumeOutcome, type Role, type ServerDeps, type ServerOptionsSource, type ServerOptionsSources, type Solution, type Storage, type SubmissionOutcome, type SubmissionRecord, type UnsafePattern, type VerifyOutcome, type WebhookHealth, type WebhookRecord, decodeSolution, encodeSolution, hashOf, mintChallenge, solveChallenge, verifySolution };
|
|
1226
|
+
export { type Action, type Actor, type AttemptOutcome, type AuditAction, type AuditDraft, type AuditEntry, type AuthDeps, type AuthOutcome, type BreakerState, CHALLENGE_TTL_SECONDS, type Challenge, type ClaimOutcome, DEFAULT_MAX_NUMBER, type DeliveryHeaders, type DeliveryRecord, type DraftMigration, type DraftRecord, type FileRecord, type FormRecord, type FormVersionRecord, type ListedForm, type ListedSubmission, type ListedVersion, type MembershipOutcome, type OfferDeps, type OfferInput, type OfferOutcome, type OutboxDeps, type PublishOutcome, type ReplayOutcome, type ResolvedForm, type ResumeOutcome, type Role, type ScanVerdict, type Scanner, type ScreenOutcome, type ServerDeps, type ServerOptionsSource, type ServerOptionsSources, type Solution, type Storage, type SubmissionOutcome, type SubmissionRecord, type UnsafePattern, type VerifyOutcome, type WebhookHealth, type WebhookRecord, decodeSolution, encodeSolution, hashOf, mintChallenge, solveChallenge, verifySolution };
|
|
1097
1227
|
//# sourceMappingURL=index.d.mts.map
|
package/dist/index.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/options-membership.ts","../src/ports.ts","../src/auth.ts","../src/audit.ts","../src/outbox.ts","../src/breaker.ts","../src/
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/options-membership.ts","../src/ports.ts","../src/auth.ts","../src/audit.ts","../src/outbox.ts","../src/breaker.ts","../src/uploads.ts","../src/deps.ts","../src/use-cases.ts","../src/redos.ts","../src/testing/memory-storage.ts","../src/address.ts","../src/webhook.ts","../src/publishing.ts"],"mappings":"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"}
|