@marktiderman/genesis-forms 0.8.0 → 0.9.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/README.md +299 -10
- package/dist/destinations.cjs +202 -39
- package/dist/destinations.cjs.map +1 -1
- package/dist/destinations.d.cts +67 -21
- package/dist/destinations.d.ts +67 -21
- package/dist/destinations.js +202 -40
- package/dist/destinations.js.map +1 -1
- package/dist/{engine-Bj23YJlB.d.cts → engine-BaHPC2-1.d.cts} +2 -2
- package/dist/{engine-CzinTJph.d.ts → engine-Da99Tv-E.d.ts} +2 -2
- package/dist/index.cjs +996 -167
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +288 -11
- package/dist/index.d.ts +288 -11
- package/dist/index.js +975 -168
- package/dist/index.js.map +1 -1
- package/dist/server.cjs +563 -173
- package/dist/server.cjs.map +1 -1
- package/dist/server.d.cts +3 -474
- package/dist/server.d.ts +3 -474
- package/dist/server.js +558 -174
- package/dist/server.js.map +1 -1
- package/dist/submit-protocol-CFi-0Qd0.d.ts +840 -0
- package/dist/submit-protocol-D0cOckmK.d.cts +840 -0
- package/dist/testing.cjs +250 -154
- package/dist/testing.cjs.map +1 -1
- package/dist/testing.d.cts +4 -4
- package/dist/testing.d.ts +4 -4
- package/dist/testing.js +250 -154
- package/dist/testing.js.map +1 -1
- package/dist/{types-jqDKcmP4.d.cts → types-C0pUfew3.d.cts} +7 -1
- package/dist/{types-jqDKcmP4.d.ts → types-C0pUfew3.d.ts} +7 -1
- package/dist/{validate-DvfQHYnu.d.ts → validate-DpmBssFk.d.ts} +258 -140
- package/dist/{validate-irIPubxc.d.cts → validate-U5LoW3eR.d.cts} +258 -140
- package/package.json +2 -2
- package/dist/upload-protocol-BOe018kL.d.cts +0 -122
- package/dist/upload-protocol-BUO74WkS.d.ts +0 -122
package/dist/index.d.cts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { a as FieldBlock, b as ContentBlock, P as PageBreakBlock, O as OptionsSource, F as FormDefinition, H as HiddenInput, c as FormLayout, L as LogicRule, E as Ending, d as FormSettings, J as Json, C as Condition, B as Block } from './types-
|
|
2
|
-
export { e as FormEvent, f as Operand, g as Operator } from './types-
|
|
3
|
-
import { P as PhotoFormat, a as PhotoRef } from './
|
|
4
|
-
export { D as DEFAULT_PHOTO_FILES,
|
|
5
|
-
import { F as FormEngine, a as FormState } from './engine-
|
|
6
|
-
export { C as CreateFormOptions, c as createForm, i as isFormEvent, b as isJson, r as replay } from './engine-
|
|
7
|
-
import { F as FieldValidator } from './validate-
|
|
8
|
-
export { A as AnswerSnapshot, C as CHOICE_FIELD_TYPES, a as ChoiceOption, b as FieldTypeManifest, H as HostOptions, L as LinkParams, M as MAX_PREFILL_PARAM_LENGTH, O as OPTION_LIMITS,
|
|
1
|
+
import { a as FieldBlock, b as ContentBlock, P as PageBreakBlock, O as OptionsSource, F as FormDefinition, H as HiddenInput, c as FormLayout, L as LogicRule, E as Ending, d as FormSettings, J as Json, C as Condition, B as Block } from './types-C0pUfew3.cjs';
|
|
2
|
+
export { e as FormEvent, f as Operand, g as Operator } from './types-C0pUfew3.cjs';
|
|
3
|
+
import { P as PhotoFormat, a as PhotoRef, S as SubmissionInput, b as SubmitRequest, c as SubmitFailureReason, d as SubmitResult } from './submit-protocol-D0cOckmK.cjs';
|
|
4
|
+
export { D as DEFAULT_PHOTO_FILES, e as DEFAULT_PHOTO_SIZE_MB, I as IDEMPOTENCY_KEY_PATTERN, M as MAX_PHOTO_FILES, f as MAX_PHOTO_ID_LENGTH, g as MAX_PHOTO_NAME_LENGTH, h as MAX_PHOTO_SIZE_MB, i as MAX_UPLOAD_REQUEST_BYTES, j as MergeOptions, k as PHOTO_FORMATS, l as PHOTO_TYPE, m as PhotoLimits, n as SignedUpload, o as SubmitOutcome, U as UPLOAD_FIELD_TYPES, p as UploadGrant, q as UploadRefusal, r as UploadRequest, s as isIdempotencyKey, t as isPhotoFormat, u as isPhotoRef, v as isSubmitResult, w as mergeResponses, x as photoCountMessage, y as photoFormatList, z as photoLimits, A as photoRefs, B as photoSizeMessage, C as photoTypeMessage, E as submitOutcome } from './submit-protocol-D0cOckmK.cjs';
|
|
5
|
+
import { F as FormEngine, a as FormState } from './engine-BaHPC2-1.cjs';
|
|
6
|
+
export { C as CreateFormOptions, c as createForm, i as isFormEvent, b as isJson, r as replay } from './engine-BaHPC2-1.cjs';
|
|
7
|
+
import { F as FieldValidator } from './validate-U5LoW3eR.cjs';
|
|
8
|
+
export { A as AnswerSnapshot, C as CHOICE_FIELD_TYPES, a as ChoiceOption, b as ContentTypeManifest, c as FieldTypeManifest, H as HostOptions, L as LinkParams, d as LiteralType, M as MAX_PREFILL_PARAM_LENGTH, O as OPTION_LIMITS, e as OTHER_MAX_LENGTH, f as OptionSnapshot, P as PrefillSetting, g as PrefillState, h as PropsCheck, i as PropsChecker, R as REQUIRED_MESSAGE, j as RegisterOptions, k as RestoredAnswers, S as SEARCHABLE_OPTION_COUNT, l as StandardSchemaV1, V as VALIDATOR_FAILED_MESSAGE, m as answerSnapshots, n as answersFromSnapshots, o as answersOnPath, p as choiceKind, q as choiceOptions, r as clearFilteredAnswers, s as contentText, t as dateLimits, u as filteredChoiceError, v as getContentType, w as getFieldType, x as hasOptionFilters, y as hiddenByPrefill, z as isBuiltInFieldType, B as isChoiceField, D as isEmptyValue, E as isHttpsUrl, G as isIsoDate, I as isOtherValue, J as isPrefillParam, K as isSearchable, N as isSensitive, Q as listContentTypes, T as listFieldTypes, U as normalizeValue, W as numberLimits, X as optionFilter, Y as optionJson, Z as optionMatches, _ as optionsSource, $ as otherOption, a0 as prefillFromLink, a1 as prefillSetting, a2 as ratingDisplay, a3 as readOption, a4 as registerContentType, a5 as registerFieldType, a6 as respondentPath, a7 as scaleLabels, a8 as searchOptions, a9 as summarizeValue, aa as validateField, ab as visibleOptions, ac as withHostOptions } from './validate-U5LoW3eR.cjs';
|
|
9
9
|
|
|
10
10
|
/** A field block that remembers its key and value type, for answer inference. */
|
|
11
11
|
type FieldSpec<K extends string = string, V = unknown> = FieldBlock & {
|
|
@@ -300,8 +300,9 @@ type ParseResult = {
|
|
|
300
300
|
/**
|
|
301
301
|
* Check a definition before it is stored or rendered: structure, the types of
|
|
302
302
|
* text fields, unique ids and keys, known field types, references that
|
|
303
|
-
* resolve, operators that fit the field type.
|
|
304
|
-
*
|
|
303
|
+
* resolve, operators that fit the field type. Each field and content kind
|
|
304
|
+
* checks its own `props` (`checkProps` in the registry). Warnings flag things
|
|
305
|
+
* that work but are probably mistakes. Never throws, whatever the input.
|
|
305
306
|
*/
|
|
306
307
|
declare function parseForm(input: unknown): ParseResult;
|
|
307
308
|
|
|
@@ -686,4 +687,280 @@ declare function describeCondition(condition: Condition, def: FormDefinition, fi
|
|
|
686
687
|
*/
|
|
687
688
|
declare function carryAnswers(from: FormDefinition, to: FormDefinition, answers: Record<string, Json>, extra?: Record<string, Json | null | undefined>): Record<string, Json>;
|
|
688
689
|
|
|
689
|
-
|
|
690
|
+
/**
|
|
691
|
+
* The key-value storage drafts and the submit outbox keep their data in. One small shape
|
|
692
|
+
* that `localStorage` (web) and React Native's AsyncStorage both are already; MMKV and
|
|
693
|
+
* others take a three-line wrapper:
|
|
694
|
+
*
|
|
695
|
+
* ```ts
|
|
696
|
+
* const storage: KeyValueStorage = {
|
|
697
|
+
* getItem: (key) => mmkv.getString(key) ?? null,
|
|
698
|
+
* setItem: (key, value) => mmkv.set(key, value),
|
|
699
|
+
* removeItem: (key) => mmkv.delete(key),
|
|
700
|
+
* };
|
|
701
|
+
* ```
|
|
702
|
+
*/
|
|
703
|
+
type MaybePromise<T> = T | Promise<T>;
|
|
704
|
+
/** Strings by key, kept across restarts. Each method may answer at once or with a promise; a throw (or a rejection) is a failure the caller reports. */
|
|
705
|
+
interface KeyValueStorage {
|
|
706
|
+
getItem(key: string): MaybePromise<string | null | undefined>;
|
|
707
|
+
setItem(key: string, value: string): MaybePromise<void>;
|
|
708
|
+
removeItem(key: string): MaybePromise<void>;
|
|
709
|
+
}
|
|
710
|
+
/** Storage that lasts as long as the page or app does: for tests, and where nothing may be kept on the device. */
|
|
711
|
+
declare function memoryStorage(): KeyValueStorage & {
|
|
712
|
+
keys(): string[];
|
|
713
|
+
};
|
|
714
|
+
/**
|
|
715
|
+
* The browser's `localStorage` (or another `Storage`, such as `sessionStorage`), looked up on
|
|
716
|
+
* each call, so creating it during server rendering is safe. Where there is none (a server, a
|
|
717
|
+
* locked-down private window) every call throws, and the caller reports it.
|
|
718
|
+
*/
|
|
719
|
+
declare function webStorage(storage?: () => Pick<Storage, "getItem" | "setItem" | "removeItem"> | undefined): KeyValueStorage;
|
|
720
|
+
|
|
721
|
+
/** Whose draft of which form, at which version of it. */
|
|
722
|
+
interface DraftKey {
|
|
723
|
+
formId: string;
|
|
724
|
+
/** The definition's version (a published version label, or `fingerprint(definition)`). */
|
|
725
|
+
version: string;
|
|
726
|
+
/** Who is answering: a user id, or a session id made once on this device for anonymous visits. */
|
|
727
|
+
respondent: string;
|
|
728
|
+
}
|
|
729
|
+
/** A saved draft. Plain JSON. */
|
|
730
|
+
interface FormDraft extends DraftKey {
|
|
731
|
+
/** The answers given, by field id, including ones logic hides now (so going back keeps them). Calculated fields are left out. */
|
|
732
|
+
values: Record<string, Json>;
|
|
733
|
+
/** Each saved field's type when it was saved, by field id, to tell whether the form still fits. */
|
|
734
|
+
types: Record<string, string>;
|
|
735
|
+
/** The furthest question reached (`formProgress(engine).fieldId`): `createForm`'s `resumeAt`. */
|
|
736
|
+
resumeAt: string | null;
|
|
737
|
+
/** When it was saved, in milliseconds since 1970. */
|
|
738
|
+
savedAt: number;
|
|
739
|
+
}
|
|
740
|
+
/** Where drafts are kept. One draft per form and respondent: saving another version replaces it. */
|
|
741
|
+
interface DraftStore {
|
|
742
|
+
/** The draft of this form by this respondent, of any version, or null. */
|
|
743
|
+
get(key: DraftKey): MaybePromise<FormDraft | null>;
|
|
744
|
+
set(draft: FormDraft): MaybePromise<void>;
|
|
745
|
+
remove(key: DraftKey): MaybePromise<void>;
|
|
746
|
+
}
|
|
747
|
+
declare const DRAFT_KEY_PREFIX = "genesis-forms:draft:";
|
|
748
|
+
/**
|
|
749
|
+
* A `DraftStore` over key-value storage, one entry per form and respondent (the version is
|
|
750
|
+
* inside it, so a new version finds the old draft and `restoreDraft` decides whether it still
|
|
751
|
+
* fits). A stored entry that doesn't read as a draft is answered null.
|
|
752
|
+
*/
|
|
753
|
+
declare function createDraftStore(storage: KeyValueStorage, options?: {
|
|
754
|
+
prefix?: string | undefined;
|
|
755
|
+
}): DraftStore;
|
|
756
|
+
interface DraftOptions {
|
|
757
|
+
/**
|
|
758
|
+
* Keep answers to sensitive fields (`isSensitive`) in the draft. Default false: they are
|
|
759
|
+
* left out, so a health detail or an ID number never sits in the device's storage, and the
|
|
760
|
+
* respondent answers them again after a restore.
|
|
761
|
+
*/
|
|
762
|
+
keepSensitive?: boolean | undefined;
|
|
763
|
+
}
|
|
764
|
+
/** The engine's answers so far as a draft (null when nothing is answered). */
|
|
765
|
+
declare function draftOf(engine: Pick<FormEngine, "definition" | "getState">, key: DraftKey, options?: DraftOptions & {
|
|
766
|
+
now?: (() => number) | undefined;
|
|
767
|
+
}): FormDraft | null;
|
|
768
|
+
/** 30 days: the default for how long a draft is kept. */
|
|
769
|
+
declare const DRAFT_MAX_AGE_MS: number;
|
|
770
|
+
type DraftRestore = {
|
|
771
|
+
ok: true;
|
|
772
|
+
/** For `createForm`'s `initialValues` (over the host's own prefill). */
|
|
773
|
+
values: Record<string, Json>;
|
|
774
|
+
/** For `createForm`'s `resumeAt`. */
|
|
775
|
+
resumeAt: string | null;
|
|
776
|
+
} | {
|
|
777
|
+
ok: false;
|
|
778
|
+
/**
|
|
779
|
+
* `version`: the form changed and an answer no longer fits (its question is gone, has
|
|
780
|
+
* another type, or the answer isn't valid for it now). `expired`: older than `maxAgeMs`.
|
|
781
|
+
*/
|
|
782
|
+
reason: "version" | "expired";
|
|
783
|
+
};
|
|
784
|
+
/**
|
|
785
|
+
* Whether a saved draft can be put back into this definition. The same version: yes. Another
|
|
786
|
+
* version: only when every saved answer still fits (its question is there, of the same type,
|
|
787
|
+
* and the answer passes its format checks); otherwise the whole draft is refused, never half
|
|
788
|
+
* put back. Pass the definition with the host's options filled in (`engine.definition`, or
|
|
789
|
+
* `withHostOptions`), so a picked option that's gone counts.
|
|
790
|
+
*/
|
|
791
|
+
declare function restoreDraft(definition: FormDefinition, draft: FormDraft, options: {
|
|
792
|
+
version: string;
|
|
793
|
+
now?: (() => number) | undefined;
|
|
794
|
+
maxAgeMs?: number | undefined;
|
|
795
|
+
}): DraftRestore;
|
|
796
|
+
interface DraftAutosaveOptions extends DraftOptions {
|
|
797
|
+
/** How long the respondent pauses before the draft is saved, in milliseconds. Default 800. */
|
|
798
|
+
delayMs?: number | undefined;
|
|
799
|
+
now?: (() => number) | undefined;
|
|
800
|
+
/** A save or removal that failed (storage full, unavailable). Default `console.warn`. Autosave never throws. */
|
|
801
|
+
onError?: ((error: unknown) => void) | undefined;
|
|
802
|
+
/** Each save that landed, with its time. */
|
|
803
|
+
onSaved?: ((draft: FormDraft) => void) | undefined;
|
|
804
|
+
/**
|
|
805
|
+
* A draft is stored for this key already (it was just restored). Answers cleared to nothing
|
|
806
|
+
* remove a stored draft; without this, only one this autosave wrote. Default false, so a
|
|
807
|
+
* draft that couldn't be read is never removed before the respondent answers anything.
|
|
808
|
+
*/
|
|
809
|
+
stored?: boolean | undefined;
|
|
810
|
+
}
|
|
811
|
+
interface DraftAutosave {
|
|
812
|
+
/** Save now whatever is waiting. Resolves when it is written (or failed, through `onError`). */
|
|
813
|
+
flush(): Promise<void>;
|
|
814
|
+
/**
|
|
815
|
+
* Stop saving and remove the draft: call it once the submission is safe (queued in the
|
|
816
|
+
* outbox, or stored). Nothing waiting is written after it.
|
|
817
|
+
*/
|
|
818
|
+
clear(): Promise<void>;
|
|
819
|
+
/** Stop listening. Whatever is waiting is saved first (`flush`). */
|
|
820
|
+
dispose(): Promise<void>;
|
|
821
|
+
}
|
|
822
|
+
/**
|
|
823
|
+
* Save the engine's answers to `store` as they change, `delayMs` after the last change, one
|
|
824
|
+
* write at a time and in order. Answers cleared to nothing remove the draft (one this
|
|
825
|
+
* autosave wrote, or the one restored: `stored`), so an emptied form doesn't come back filled.
|
|
826
|
+
*/
|
|
827
|
+
declare function createDraftAutosave(engine: Pick<FormEngine, "definition" | "getState" | "subscribe">, store: DraftStore, key: DraftKey, options?: DraftAutosaveOptions): DraftAutosave;
|
|
828
|
+
|
|
829
|
+
/**
|
|
830
|
+
* The submit outbox: submissions wait on the device until the server has them. Each is
|
|
831
|
+
* queued with an idempotency key (so a retry is stored once), kept in storage across
|
|
832
|
+
* restarts, sent when online with backoff between tries, and never dropped silently: one the
|
|
833
|
+
* server refuses, or that runs out of tries, stays as `failed` until the respondent retries
|
|
834
|
+
* or discards it.
|
|
835
|
+
*
|
|
836
|
+
* Transport-agnostic: `send` is the app's, typically a `fetch` to its endpoint that runs
|
|
837
|
+
* `submitResponse` and answers its `SubmitResult` as JSON.
|
|
838
|
+
*/
|
|
839
|
+
|
|
840
|
+
/** One queued submission, as stored. Plain JSON. */
|
|
841
|
+
interface OutboxEntry {
|
|
842
|
+
/** The submission's idempotency key. */
|
|
843
|
+
id: string;
|
|
844
|
+
/** Which form it is for: `send` picks the endpoint by it. */
|
|
845
|
+
formId: string;
|
|
846
|
+
/** The body sent, every time the same. */
|
|
847
|
+
request: SubmitRequest;
|
|
848
|
+
/** The app's own details to show in a list (a form's title), never sent. */
|
|
849
|
+
meta: Record<string, Json> | null;
|
|
850
|
+
createdAt: number;
|
|
851
|
+
/** Tries so far (since the last `retry`). */
|
|
852
|
+
attempts: number;
|
|
853
|
+
/** When it may be tried next. */
|
|
854
|
+
nextAttemptAt: number;
|
|
855
|
+
/** `pending`: will be sent. `failed`: refused, or out of tries; waits for `retry` or `discard`. */
|
|
856
|
+
state: "pending" | "failed";
|
|
857
|
+
/** Why the last try didn't deliver it, for the respondent or a log. Never holds an answer. */
|
|
858
|
+
lastError: string | null;
|
|
859
|
+
/** When the server refused it (`invalid` or `closed`), what it said. */
|
|
860
|
+
rejection: {
|
|
861
|
+
reason: SubmitFailureReason;
|
|
862
|
+
errors?: Record<string, string>;
|
|
863
|
+
} | null;
|
|
864
|
+
}
|
|
865
|
+
/** A queued submission as the outbox shows it. */
|
|
866
|
+
interface OutboxItem extends OutboxEntry {
|
|
867
|
+
/** Being sent now. */
|
|
868
|
+
sending: boolean;
|
|
869
|
+
}
|
|
870
|
+
interface OutboxSnapshot {
|
|
871
|
+
/** Whether what was stored has been read. Until then `items` may be missing some. */
|
|
872
|
+
loaded: boolean;
|
|
873
|
+
online: boolean;
|
|
874
|
+
/** Oldest first. */
|
|
875
|
+
items: readonly OutboxItem[];
|
|
876
|
+
pending: number;
|
|
877
|
+
failed: number;
|
|
878
|
+
/** Reading or writing the storage failed; null once it works again. */
|
|
879
|
+
storageError: string | null;
|
|
880
|
+
/** Stored submissions that couldn't be read back, by id: left in storage, never sent or removed. */
|
|
881
|
+
unreadable: readonly string[];
|
|
882
|
+
}
|
|
883
|
+
interface EnqueueInput {
|
|
884
|
+
formId: string;
|
|
885
|
+
submission: SubmissionInput;
|
|
886
|
+
/** Default: a new UUID. Pass the same key again and the queued submission is returned, not queued twice. */
|
|
887
|
+
idempotencyKey?: string | undefined;
|
|
888
|
+
/** Default: now. */
|
|
889
|
+
clientSubmittedAt?: number | undefined;
|
|
890
|
+
/** The visit's attempt id, for a form with upload fields (`SubmitRequest.attemptId`). */
|
|
891
|
+
attemptId?: string | undefined;
|
|
892
|
+
meta?: Record<string, Json> | undefined;
|
|
893
|
+
}
|
|
894
|
+
interface SubmitOutboxOptions {
|
|
895
|
+
storage: KeyValueStorage;
|
|
896
|
+
/**
|
|
897
|
+
* Send one submission and answer what the server said: its `SubmitResult` (an endpoint's
|
|
898
|
+
* JSON body, whatever its status code). Throw, or answer anything else, and it is tried
|
|
899
|
+
* again later. Abort on `signal` (sent after `timeoutMs`).
|
|
900
|
+
*/
|
|
901
|
+
send(item: OutboxItem, context: {
|
|
902
|
+
signal: AbortSignal;
|
|
903
|
+
}): Promise<unknown>;
|
|
904
|
+
/** Milliseconds since 1970. Default `Date.now`. */
|
|
905
|
+
now?: (() => number) | undefined;
|
|
906
|
+
/** Storage keys start with this. Default `genesis-forms:outbox`. One outbox per prefix. */
|
|
907
|
+
prefix?: string | undefined;
|
|
908
|
+
/**
|
|
909
|
+
* Tries before a submission is marked `failed` (it is kept). Default 10. A submission that
|
|
910
|
+
* ran out of tries (not one the server refused) is tried again from the start each time the
|
|
911
|
+
* outbox starts and each time the device comes back online.
|
|
912
|
+
*/
|
|
913
|
+
maxAttempts?: number | undefined;
|
|
914
|
+
/** Wait before try n+1: about `baseMs * 2^(n-1)`, at most `maxMs`, with jitter. Default 2 seconds and 15 minutes. */
|
|
915
|
+
backoff?: {
|
|
916
|
+
baseMs?: number | undefined;
|
|
917
|
+
maxMs?: number | undefined;
|
|
918
|
+
} | undefined;
|
|
919
|
+
/** How long one send may take before it is aborted and tried again. Default 30 seconds. */
|
|
920
|
+
timeoutMs?: number | undefined;
|
|
921
|
+
/** Whether the device is online to begin with. Default true. Keep it current with `setOnline`. */
|
|
922
|
+
online?: boolean | undefined;
|
|
923
|
+
/**
|
|
924
|
+
* Makes idempotency keys. Default `crypto.randomUUID`; where there is none (React Native
|
|
925
|
+
* without a polyfill) pass one (expo-crypto's `randomUUID`), or `createSubmitOutbox` throws.
|
|
926
|
+
*/
|
|
927
|
+
createKey?: (() => string) | undefined;
|
|
928
|
+
/** 0 to 1, for jitter. Default `Math.random`. */
|
|
929
|
+
random?: (() => number) | undefined;
|
|
930
|
+
/** A submission the server now has (stored now, or before: a duplicate). */
|
|
931
|
+
onDelivered?: ((item: OutboxItem, result: SubmitResult) => void) | undefined;
|
|
932
|
+
/** Something went wrong that the app may want to log. Default `console.warn`. */
|
|
933
|
+
log?: ((message: string) => void) | undefined;
|
|
934
|
+
}
|
|
935
|
+
interface SubmitOutbox {
|
|
936
|
+
/** Resolves once stored submissions are read (or reading failed: see `storageError`). Never rejects. */
|
|
937
|
+
readonly ready: Promise<void>;
|
|
938
|
+
/**
|
|
939
|
+
* Queue a submission. Resolves once it is in storage: only then is it safe to clear the
|
|
940
|
+
* draft. Rejects when storage can't take it (keep the draft, and tell the respondent).
|
|
941
|
+
* Sends it at once when the outbox is started and online.
|
|
942
|
+
*/
|
|
943
|
+
enqueue(input: EnqueueInput): Promise<OutboxItem>;
|
|
944
|
+
/** Send every pending submission that is due, oldest first, one at a time. */
|
|
945
|
+
flush(): Promise<void>;
|
|
946
|
+
/** Try a failed submission again from its first try, now. */
|
|
947
|
+
retry(id: string): Promise<void>;
|
|
948
|
+
/** Remove a queued submission for good. False while it is being sent, or when it isn't there. */
|
|
949
|
+
discard(id: string): Promise<boolean>;
|
|
950
|
+
/** Tell the outbox whether the device is online. Coming online sends what's pending at once. */
|
|
951
|
+
setOnline(online: boolean): void;
|
|
952
|
+
/** Send as submissions come due (timers). `stop` ends it. */
|
|
953
|
+
start(): void;
|
|
954
|
+
stop(): void;
|
|
955
|
+
getSnapshot(): OutboxSnapshot;
|
|
956
|
+
subscribe(listener: () => void): () => void;
|
|
957
|
+
}
|
|
958
|
+
/**
|
|
959
|
+
* Each submission is stored under its own key, with an index of keys beside it. The index is
|
|
960
|
+
* read again and merged on every change, so two outboxes on one storage (two tabs) don't
|
|
961
|
+
* write each other's submissions out; both may send one, and its idempotency key makes the
|
|
962
|
+
* server store it once.
|
|
963
|
+
*/
|
|
964
|
+
declare function createSubmitOutbox(options: SubmitOutboxOptions): SubmitOutbox;
|
|
965
|
+
|
|
966
|
+
export { type AnswersOf, Block, CALCULATED_TYPE, COMPUTED_FIELD_TYPES, type CalculatedFormat, type CalculatedProps, type ChoiceFieldOptions, type CoerceHiddenOptions, type CoercedHidden, Condition, ContentBlock, DRAFT_KEY_PREFIX, DRAFT_MAX_AGE_MS, type DraftAutosave, type DraftAutosaveOptions, type DraftKey, type DraftOptions, type DraftRestore, type DraftStore, Ending, type EnqueueInput, type ExplainInput, type ExplainOptions, type Explanation, FORMULA_FUNCTIONS, FieldBlock, type FieldOptions, type FieldSpec, type FieldValidation, FieldValidator, type Form, FormDefinition, type FormDraft, FormEngine, type FormInput, type FormIssue, FormLayout, type FormProgress, FormSettings, FormState, type FormulaFunction, type FormulaNode, type FormulaParseResult, type FormulaReader, type FormulaRef, type FormulaValue, HIGHLIGHT_NOTE_MESSAGE, HiddenInput, type Highlight, Json, type JumpTrace, type KeyResolver, type KeyValueStorage, LogicRule, MAX_CALCULATED_DECIMALS, MAX_FORMULA_ITEMS, MAX_FORMULA_LENGTH, MAX_FORM_FORMULA_LENGTH, MAX_HIDDEN_STRING_LENGTH, MAX_HIGHLIGHT_NOTE_LENGTH, MAX_HIGHLIGHT_QUOTE_LENGTH, MAX_SETTLE_ITEMS, MAX_TABLE_CELL_LENGTH, MAX_TABLE_COLUMNS, MAX_TIMESTAMP_SECONDS, type MaybePromise, type MediaNote, OptionsSource, type OutboxEntry, type OutboxItem, type OutboxSnapshot, PageBreakBlock, type ParseResult, type PathResult, PhotoFormat, PhotoRef, type RuleSpec, type RuleTrace, STRUCTURED_FIELD_TYPES, SubmitFailureReason, type SubmitOutbox, type SubmitOutboxOptions, SubmitRequest, SubmitResult, type TableColumn, calculatedProps, calculatedValue, canonicalJson, carryAnswers, coerceHidden, coerceHiddenValue, computePath, createDraftAutosave, createDraftStore, createSubmitOutbox, defineForm, describeCondition, draftOf, evaluate, evaluateFormula, explain, f, fingerprint, formProgress, formatCalculated, formatTimestamp, formulaInput, formulaNumber, formulaRefs, furthestReached, listLimits, memoryStorage, parseForm, parseFormula, parseTimestamp, recall, renameFormulaKey, restoreDraft, ruleVisible, steps, tableColumns, tableLimits, webStorage, when };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { a as FieldBlock, b as ContentBlock, P as PageBreakBlock, O as OptionsSource, F as FormDefinition, H as HiddenInput, c as FormLayout, L as LogicRule, E as Ending, d as FormSettings, J as Json, C as Condition, B as Block } from './types-
|
|
2
|
-
export { e as FormEvent, f as Operand, g as Operator } from './types-
|
|
3
|
-
import { P as PhotoFormat, a as PhotoRef } from './
|
|
4
|
-
export { D as DEFAULT_PHOTO_FILES,
|
|
5
|
-
import { F as FormEngine, a as FormState } from './engine-
|
|
6
|
-
export { C as CreateFormOptions, c as createForm, i as isFormEvent, b as isJson, r as replay } from './engine-
|
|
7
|
-
import { F as FieldValidator } from './validate-
|
|
8
|
-
export { A as AnswerSnapshot, C as CHOICE_FIELD_TYPES, a as ChoiceOption, b as FieldTypeManifest, H as HostOptions, L as LinkParams, M as MAX_PREFILL_PARAM_LENGTH, O as OPTION_LIMITS,
|
|
1
|
+
import { a as FieldBlock, b as ContentBlock, P as PageBreakBlock, O as OptionsSource, F as FormDefinition, H as HiddenInput, c as FormLayout, L as LogicRule, E as Ending, d as FormSettings, J as Json, C as Condition, B as Block } from './types-C0pUfew3.js';
|
|
2
|
+
export { e as FormEvent, f as Operand, g as Operator } from './types-C0pUfew3.js';
|
|
3
|
+
import { P as PhotoFormat, a as PhotoRef, S as SubmissionInput, b as SubmitRequest, c as SubmitFailureReason, d as SubmitResult } from './submit-protocol-CFi-0Qd0.js';
|
|
4
|
+
export { D as DEFAULT_PHOTO_FILES, e as DEFAULT_PHOTO_SIZE_MB, I as IDEMPOTENCY_KEY_PATTERN, M as MAX_PHOTO_FILES, f as MAX_PHOTO_ID_LENGTH, g as MAX_PHOTO_NAME_LENGTH, h as MAX_PHOTO_SIZE_MB, i as MAX_UPLOAD_REQUEST_BYTES, j as MergeOptions, k as PHOTO_FORMATS, l as PHOTO_TYPE, m as PhotoLimits, n as SignedUpload, o as SubmitOutcome, U as UPLOAD_FIELD_TYPES, p as UploadGrant, q as UploadRefusal, r as UploadRequest, s as isIdempotencyKey, t as isPhotoFormat, u as isPhotoRef, v as isSubmitResult, w as mergeResponses, x as photoCountMessage, y as photoFormatList, z as photoLimits, A as photoRefs, B as photoSizeMessage, C as photoTypeMessage, E as submitOutcome } from './submit-protocol-CFi-0Qd0.js';
|
|
5
|
+
import { F as FormEngine, a as FormState } from './engine-Da99Tv-E.js';
|
|
6
|
+
export { C as CreateFormOptions, c as createForm, i as isFormEvent, b as isJson, r as replay } from './engine-Da99Tv-E.js';
|
|
7
|
+
import { F as FieldValidator } from './validate-DpmBssFk.js';
|
|
8
|
+
export { A as AnswerSnapshot, C as CHOICE_FIELD_TYPES, a as ChoiceOption, b as ContentTypeManifest, c as FieldTypeManifest, H as HostOptions, L as LinkParams, d as LiteralType, M as MAX_PREFILL_PARAM_LENGTH, O as OPTION_LIMITS, e as OTHER_MAX_LENGTH, f as OptionSnapshot, P as PrefillSetting, g as PrefillState, h as PropsCheck, i as PropsChecker, R as REQUIRED_MESSAGE, j as RegisterOptions, k as RestoredAnswers, S as SEARCHABLE_OPTION_COUNT, l as StandardSchemaV1, V as VALIDATOR_FAILED_MESSAGE, m as answerSnapshots, n as answersFromSnapshots, o as answersOnPath, p as choiceKind, q as choiceOptions, r as clearFilteredAnswers, s as contentText, t as dateLimits, u as filteredChoiceError, v as getContentType, w as getFieldType, x as hasOptionFilters, y as hiddenByPrefill, z as isBuiltInFieldType, B as isChoiceField, D as isEmptyValue, E as isHttpsUrl, G as isIsoDate, I as isOtherValue, J as isPrefillParam, K as isSearchable, N as isSensitive, Q as listContentTypes, T as listFieldTypes, U as normalizeValue, W as numberLimits, X as optionFilter, Y as optionJson, Z as optionMatches, _ as optionsSource, $ as otherOption, a0 as prefillFromLink, a1 as prefillSetting, a2 as ratingDisplay, a3 as readOption, a4 as registerContentType, a5 as registerFieldType, a6 as respondentPath, a7 as scaleLabels, a8 as searchOptions, a9 as summarizeValue, aa as validateField, ab as visibleOptions, ac as withHostOptions } from './validate-DpmBssFk.js';
|
|
9
9
|
|
|
10
10
|
/** A field block that remembers its key and value type, for answer inference. */
|
|
11
11
|
type FieldSpec<K extends string = string, V = unknown> = FieldBlock & {
|
|
@@ -300,8 +300,9 @@ type ParseResult = {
|
|
|
300
300
|
/**
|
|
301
301
|
* Check a definition before it is stored or rendered: structure, the types of
|
|
302
302
|
* text fields, unique ids and keys, known field types, references that
|
|
303
|
-
* resolve, operators that fit the field type.
|
|
304
|
-
*
|
|
303
|
+
* resolve, operators that fit the field type. Each field and content kind
|
|
304
|
+
* checks its own `props` (`checkProps` in the registry). Warnings flag things
|
|
305
|
+
* that work but are probably mistakes. Never throws, whatever the input.
|
|
305
306
|
*/
|
|
306
307
|
declare function parseForm(input: unknown): ParseResult;
|
|
307
308
|
|
|
@@ -686,4 +687,280 @@ declare function describeCondition(condition: Condition, def: FormDefinition, fi
|
|
|
686
687
|
*/
|
|
687
688
|
declare function carryAnswers(from: FormDefinition, to: FormDefinition, answers: Record<string, Json>, extra?: Record<string, Json | null | undefined>): Record<string, Json>;
|
|
688
689
|
|
|
689
|
-
|
|
690
|
+
/**
|
|
691
|
+
* The key-value storage drafts and the submit outbox keep their data in. One small shape
|
|
692
|
+
* that `localStorage` (web) and React Native's AsyncStorage both are already; MMKV and
|
|
693
|
+
* others take a three-line wrapper:
|
|
694
|
+
*
|
|
695
|
+
* ```ts
|
|
696
|
+
* const storage: KeyValueStorage = {
|
|
697
|
+
* getItem: (key) => mmkv.getString(key) ?? null,
|
|
698
|
+
* setItem: (key, value) => mmkv.set(key, value),
|
|
699
|
+
* removeItem: (key) => mmkv.delete(key),
|
|
700
|
+
* };
|
|
701
|
+
* ```
|
|
702
|
+
*/
|
|
703
|
+
type MaybePromise<T> = T | Promise<T>;
|
|
704
|
+
/** Strings by key, kept across restarts. Each method may answer at once or with a promise; a throw (or a rejection) is a failure the caller reports. */
|
|
705
|
+
interface KeyValueStorage {
|
|
706
|
+
getItem(key: string): MaybePromise<string | null | undefined>;
|
|
707
|
+
setItem(key: string, value: string): MaybePromise<void>;
|
|
708
|
+
removeItem(key: string): MaybePromise<void>;
|
|
709
|
+
}
|
|
710
|
+
/** Storage that lasts as long as the page or app does: for tests, and where nothing may be kept on the device. */
|
|
711
|
+
declare function memoryStorage(): KeyValueStorage & {
|
|
712
|
+
keys(): string[];
|
|
713
|
+
};
|
|
714
|
+
/**
|
|
715
|
+
* The browser's `localStorage` (or another `Storage`, such as `sessionStorage`), looked up on
|
|
716
|
+
* each call, so creating it during server rendering is safe. Where there is none (a server, a
|
|
717
|
+
* locked-down private window) every call throws, and the caller reports it.
|
|
718
|
+
*/
|
|
719
|
+
declare function webStorage(storage?: () => Pick<Storage, "getItem" | "setItem" | "removeItem"> | undefined): KeyValueStorage;
|
|
720
|
+
|
|
721
|
+
/** Whose draft of which form, at which version of it. */
|
|
722
|
+
interface DraftKey {
|
|
723
|
+
formId: string;
|
|
724
|
+
/** The definition's version (a published version label, or `fingerprint(definition)`). */
|
|
725
|
+
version: string;
|
|
726
|
+
/** Who is answering: a user id, or a session id made once on this device for anonymous visits. */
|
|
727
|
+
respondent: string;
|
|
728
|
+
}
|
|
729
|
+
/** A saved draft. Plain JSON. */
|
|
730
|
+
interface FormDraft extends DraftKey {
|
|
731
|
+
/** The answers given, by field id, including ones logic hides now (so going back keeps them). Calculated fields are left out. */
|
|
732
|
+
values: Record<string, Json>;
|
|
733
|
+
/** Each saved field's type when it was saved, by field id, to tell whether the form still fits. */
|
|
734
|
+
types: Record<string, string>;
|
|
735
|
+
/** The furthest question reached (`formProgress(engine).fieldId`): `createForm`'s `resumeAt`. */
|
|
736
|
+
resumeAt: string | null;
|
|
737
|
+
/** When it was saved, in milliseconds since 1970. */
|
|
738
|
+
savedAt: number;
|
|
739
|
+
}
|
|
740
|
+
/** Where drafts are kept. One draft per form and respondent: saving another version replaces it. */
|
|
741
|
+
interface DraftStore {
|
|
742
|
+
/** The draft of this form by this respondent, of any version, or null. */
|
|
743
|
+
get(key: DraftKey): MaybePromise<FormDraft | null>;
|
|
744
|
+
set(draft: FormDraft): MaybePromise<void>;
|
|
745
|
+
remove(key: DraftKey): MaybePromise<void>;
|
|
746
|
+
}
|
|
747
|
+
declare const DRAFT_KEY_PREFIX = "genesis-forms:draft:";
|
|
748
|
+
/**
|
|
749
|
+
* A `DraftStore` over key-value storage, one entry per form and respondent (the version is
|
|
750
|
+
* inside it, so a new version finds the old draft and `restoreDraft` decides whether it still
|
|
751
|
+
* fits). A stored entry that doesn't read as a draft is answered null.
|
|
752
|
+
*/
|
|
753
|
+
declare function createDraftStore(storage: KeyValueStorage, options?: {
|
|
754
|
+
prefix?: string | undefined;
|
|
755
|
+
}): DraftStore;
|
|
756
|
+
interface DraftOptions {
|
|
757
|
+
/**
|
|
758
|
+
* Keep answers to sensitive fields (`isSensitive`) in the draft. Default false: they are
|
|
759
|
+
* left out, so a health detail or an ID number never sits in the device's storage, and the
|
|
760
|
+
* respondent answers them again after a restore.
|
|
761
|
+
*/
|
|
762
|
+
keepSensitive?: boolean | undefined;
|
|
763
|
+
}
|
|
764
|
+
/** The engine's answers so far as a draft (null when nothing is answered). */
|
|
765
|
+
declare function draftOf(engine: Pick<FormEngine, "definition" | "getState">, key: DraftKey, options?: DraftOptions & {
|
|
766
|
+
now?: (() => number) | undefined;
|
|
767
|
+
}): FormDraft | null;
|
|
768
|
+
/** 30 days: the default for how long a draft is kept. */
|
|
769
|
+
declare const DRAFT_MAX_AGE_MS: number;
|
|
770
|
+
type DraftRestore = {
|
|
771
|
+
ok: true;
|
|
772
|
+
/** For `createForm`'s `initialValues` (over the host's own prefill). */
|
|
773
|
+
values: Record<string, Json>;
|
|
774
|
+
/** For `createForm`'s `resumeAt`. */
|
|
775
|
+
resumeAt: string | null;
|
|
776
|
+
} | {
|
|
777
|
+
ok: false;
|
|
778
|
+
/**
|
|
779
|
+
* `version`: the form changed and an answer no longer fits (its question is gone, has
|
|
780
|
+
* another type, or the answer isn't valid for it now). `expired`: older than `maxAgeMs`.
|
|
781
|
+
*/
|
|
782
|
+
reason: "version" | "expired";
|
|
783
|
+
};
|
|
784
|
+
/**
|
|
785
|
+
* Whether a saved draft can be put back into this definition. The same version: yes. Another
|
|
786
|
+
* version: only when every saved answer still fits (its question is there, of the same type,
|
|
787
|
+
* and the answer passes its format checks); otherwise the whole draft is refused, never half
|
|
788
|
+
* put back. Pass the definition with the host's options filled in (`engine.definition`, or
|
|
789
|
+
* `withHostOptions`), so a picked option that's gone counts.
|
|
790
|
+
*/
|
|
791
|
+
declare function restoreDraft(definition: FormDefinition, draft: FormDraft, options: {
|
|
792
|
+
version: string;
|
|
793
|
+
now?: (() => number) | undefined;
|
|
794
|
+
maxAgeMs?: number | undefined;
|
|
795
|
+
}): DraftRestore;
|
|
796
|
+
interface DraftAutosaveOptions extends DraftOptions {
|
|
797
|
+
/** How long the respondent pauses before the draft is saved, in milliseconds. Default 800. */
|
|
798
|
+
delayMs?: number | undefined;
|
|
799
|
+
now?: (() => number) | undefined;
|
|
800
|
+
/** A save or removal that failed (storage full, unavailable). Default `console.warn`. Autosave never throws. */
|
|
801
|
+
onError?: ((error: unknown) => void) | undefined;
|
|
802
|
+
/** Each save that landed, with its time. */
|
|
803
|
+
onSaved?: ((draft: FormDraft) => void) | undefined;
|
|
804
|
+
/**
|
|
805
|
+
* A draft is stored for this key already (it was just restored). Answers cleared to nothing
|
|
806
|
+
* remove a stored draft; without this, only one this autosave wrote. Default false, so a
|
|
807
|
+
* draft that couldn't be read is never removed before the respondent answers anything.
|
|
808
|
+
*/
|
|
809
|
+
stored?: boolean | undefined;
|
|
810
|
+
}
|
|
811
|
+
interface DraftAutosave {
|
|
812
|
+
/** Save now whatever is waiting. Resolves when it is written (or failed, through `onError`). */
|
|
813
|
+
flush(): Promise<void>;
|
|
814
|
+
/**
|
|
815
|
+
* Stop saving and remove the draft: call it once the submission is safe (queued in the
|
|
816
|
+
* outbox, or stored). Nothing waiting is written after it.
|
|
817
|
+
*/
|
|
818
|
+
clear(): Promise<void>;
|
|
819
|
+
/** Stop listening. Whatever is waiting is saved first (`flush`). */
|
|
820
|
+
dispose(): Promise<void>;
|
|
821
|
+
}
|
|
822
|
+
/**
|
|
823
|
+
* Save the engine's answers to `store` as they change, `delayMs` after the last change, one
|
|
824
|
+
* write at a time and in order. Answers cleared to nothing remove the draft (one this
|
|
825
|
+
* autosave wrote, or the one restored: `stored`), so an emptied form doesn't come back filled.
|
|
826
|
+
*/
|
|
827
|
+
declare function createDraftAutosave(engine: Pick<FormEngine, "definition" | "getState" | "subscribe">, store: DraftStore, key: DraftKey, options?: DraftAutosaveOptions): DraftAutosave;
|
|
828
|
+
|
|
829
|
+
/**
|
|
830
|
+
* The submit outbox: submissions wait on the device until the server has them. Each is
|
|
831
|
+
* queued with an idempotency key (so a retry is stored once), kept in storage across
|
|
832
|
+
* restarts, sent when online with backoff between tries, and never dropped silently: one the
|
|
833
|
+
* server refuses, or that runs out of tries, stays as `failed` until the respondent retries
|
|
834
|
+
* or discards it.
|
|
835
|
+
*
|
|
836
|
+
* Transport-agnostic: `send` is the app's, typically a `fetch` to its endpoint that runs
|
|
837
|
+
* `submitResponse` and answers its `SubmitResult` as JSON.
|
|
838
|
+
*/
|
|
839
|
+
|
|
840
|
+
/** One queued submission, as stored. Plain JSON. */
|
|
841
|
+
interface OutboxEntry {
|
|
842
|
+
/** The submission's idempotency key. */
|
|
843
|
+
id: string;
|
|
844
|
+
/** Which form it is for: `send` picks the endpoint by it. */
|
|
845
|
+
formId: string;
|
|
846
|
+
/** The body sent, every time the same. */
|
|
847
|
+
request: SubmitRequest;
|
|
848
|
+
/** The app's own details to show in a list (a form's title), never sent. */
|
|
849
|
+
meta: Record<string, Json> | null;
|
|
850
|
+
createdAt: number;
|
|
851
|
+
/** Tries so far (since the last `retry`). */
|
|
852
|
+
attempts: number;
|
|
853
|
+
/** When it may be tried next. */
|
|
854
|
+
nextAttemptAt: number;
|
|
855
|
+
/** `pending`: will be sent. `failed`: refused, or out of tries; waits for `retry` or `discard`. */
|
|
856
|
+
state: "pending" | "failed";
|
|
857
|
+
/** Why the last try didn't deliver it, for the respondent or a log. Never holds an answer. */
|
|
858
|
+
lastError: string | null;
|
|
859
|
+
/** When the server refused it (`invalid` or `closed`), what it said. */
|
|
860
|
+
rejection: {
|
|
861
|
+
reason: SubmitFailureReason;
|
|
862
|
+
errors?: Record<string, string>;
|
|
863
|
+
} | null;
|
|
864
|
+
}
|
|
865
|
+
/** A queued submission as the outbox shows it. */
|
|
866
|
+
interface OutboxItem extends OutboxEntry {
|
|
867
|
+
/** Being sent now. */
|
|
868
|
+
sending: boolean;
|
|
869
|
+
}
|
|
870
|
+
interface OutboxSnapshot {
|
|
871
|
+
/** Whether what was stored has been read. Until then `items` may be missing some. */
|
|
872
|
+
loaded: boolean;
|
|
873
|
+
online: boolean;
|
|
874
|
+
/** Oldest first. */
|
|
875
|
+
items: readonly OutboxItem[];
|
|
876
|
+
pending: number;
|
|
877
|
+
failed: number;
|
|
878
|
+
/** Reading or writing the storage failed; null once it works again. */
|
|
879
|
+
storageError: string | null;
|
|
880
|
+
/** Stored submissions that couldn't be read back, by id: left in storage, never sent or removed. */
|
|
881
|
+
unreadable: readonly string[];
|
|
882
|
+
}
|
|
883
|
+
interface EnqueueInput {
|
|
884
|
+
formId: string;
|
|
885
|
+
submission: SubmissionInput;
|
|
886
|
+
/** Default: a new UUID. Pass the same key again and the queued submission is returned, not queued twice. */
|
|
887
|
+
idempotencyKey?: string | undefined;
|
|
888
|
+
/** Default: now. */
|
|
889
|
+
clientSubmittedAt?: number | undefined;
|
|
890
|
+
/** The visit's attempt id, for a form with upload fields (`SubmitRequest.attemptId`). */
|
|
891
|
+
attemptId?: string | undefined;
|
|
892
|
+
meta?: Record<string, Json> | undefined;
|
|
893
|
+
}
|
|
894
|
+
interface SubmitOutboxOptions {
|
|
895
|
+
storage: KeyValueStorage;
|
|
896
|
+
/**
|
|
897
|
+
* Send one submission and answer what the server said: its `SubmitResult` (an endpoint's
|
|
898
|
+
* JSON body, whatever its status code). Throw, or answer anything else, and it is tried
|
|
899
|
+
* again later. Abort on `signal` (sent after `timeoutMs`).
|
|
900
|
+
*/
|
|
901
|
+
send(item: OutboxItem, context: {
|
|
902
|
+
signal: AbortSignal;
|
|
903
|
+
}): Promise<unknown>;
|
|
904
|
+
/** Milliseconds since 1970. Default `Date.now`. */
|
|
905
|
+
now?: (() => number) | undefined;
|
|
906
|
+
/** Storage keys start with this. Default `genesis-forms:outbox`. One outbox per prefix. */
|
|
907
|
+
prefix?: string | undefined;
|
|
908
|
+
/**
|
|
909
|
+
* Tries before a submission is marked `failed` (it is kept). Default 10. A submission that
|
|
910
|
+
* ran out of tries (not one the server refused) is tried again from the start each time the
|
|
911
|
+
* outbox starts and each time the device comes back online.
|
|
912
|
+
*/
|
|
913
|
+
maxAttempts?: number | undefined;
|
|
914
|
+
/** Wait before try n+1: about `baseMs * 2^(n-1)`, at most `maxMs`, with jitter. Default 2 seconds and 15 minutes. */
|
|
915
|
+
backoff?: {
|
|
916
|
+
baseMs?: number | undefined;
|
|
917
|
+
maxMs?: number | undefined;
|
|
918
|
+
} | undefined;
|
|
919
|
+
/** How long one send may take before it is aborted and tried again. Default 30 seconds. */
|
|
920
|
+
timeoutMs?: number | undefined;
|
|
921
|
+
/** Whether the device is online to begin with. Default true. Keep it current with `setOnline`. */
|
|
922
|
+
online?: boolean | undefined;
|
|
923
|
+
/**
|
|
924
|
+
* Makes idempotency keys. Default `crypto.randomUUID`; where there is none (React Native
|
|
925
|
+
* without a polyfill) pass one (expo-crypto's `randomUUID`), or `createSubmitOutbox` throws.
|
|
926
|
+
*/
|
|
927
|
+
createKey?: (() => string) | undefined;
|
|
928
|
+
/** 0 to 1, for jitter. Default `Math.random`. */
|
|
929
|
+
random?: (() => number) | undefined;
|
|
930
|
+
/** A submission the server now has (stored now, or before: a duplicate). */
|
|
931
|
+
onDelivered?: ((item: OutboxItem, result: SubmitResult) => void) | undefined;
|
|
932
|
+
/** Something went wrong that the app may want to log. Default `console.warn`. */
|
|
933
|
+
log?: ((message: string) => void) | undefined;
|
|
934
|
+
}
|
|
935
|
+
interface SubmitOutbox {
|
|
936
|
+
/** Resolves once stored submissions are read (or reading failed: see `storageError`). Never rejects. */
|
|
937
|
+
readonly ready: Promise<void>;
|
|
938
|
+
/**
|
|
939
|
+
* Queue a submission. Resolves once it is in storage: only then is it safe to clear the
|
|
940
|
+
* draft. Rejects when storage can't take it (keep the draft, and tell the respondent).
|
|
941
|
+
* Sends it at once when the outbox is started and online.
|
|
942
|
+
*/
|
|
943
|
+
enqueue(input: EnqueueInput): Promise<OutboxItem>;
|
|
944
|
+
/** Send every pending submission that is due, oldest first, one at a time. */
|
|
945
|
+
flush(): Promise<void>;
|
|
946
|
+
/** Try a failed submission again from its first try, now. */
|
|
947
|
+
retry(id: string): Promise<void>;
|
|
948
|
+
/** Remove a queued submission for good. False while it is being sent, or when it isn't there. */
|
|
949
|
+
discard(id: string): Promise<boolean>;
|
|
950
|
+
/** Tell the outbox whether the device is online. Coming online sends what's pending at once. */
|
|
951
|
+
setOnline(online: boolean): void;
|
|
952
|
+
/** Send as submissions come due (timers). `stop` ends it. */
|
|
953
|
+
start(): void;
|
|
954
|
+
stop(): void;
|
|
955
|
+
getSnapshot(): OutboxSnapshot;
|
|
956
|
+
subscribe(listener: () => void): () => void;
|
|
957
|
+
}
|
|
958
|
+
/**
|
|
959
|
+
* Each submission is stored under its own key, with an index of keys beside it. The index is
|
|
960
|
+
* read again and merged on every change, so two outboxes on one storage (two tabs) don't
|
|
961
|
+
* write each other's submissions out; both may send one, and its idempotency key makes the
|
|
962
|
+
* server store it once.
|
|
963
|
+
*/
|
|
964
|
+
declare function createSubmitOutbox(options: SubmitOutboxOptions): SubmitOutbox;
|
|
965
|
+
|
|
966
|
+
export { type AnswersOf, Block, CALCULATED_TYPE, COMPUTED_FIELD_TYPES, type CalculatedFormat, type CalculatedProps, type ChoiceFieldOptions, type CoerceHiddenOptions, type CoercedHidden, Condition, ContentBlock, DRAFT_KEY_PREFIX, DRAFT_MAX_AGE_MS, type DraftAutosave, type DraftAutosaveOptions, type DraftKey, type DraftOptions, type DraftRestore, type DraftStore, Ending, type EnqueueInput, type ExplainInput, type ExplainOptions, type Explanation, FORMULA_FUNCTIONS, FieldBlock, type FieldOptions, type FieldSpec, type FieldValidation, FieldValidator, type Form, FormDefinition, type FormDraft, FormEngine, type FormInput, type FormIssue, FormLayout, type FormProgress, FormSettings, FormState, type FormulaFunction, type FormulaNode, type FormulaParseResult, type FormulaReader, type FormulaRef, type FormulaValue, HIGHLIGHT_NOTE_MESSAGE, HiddenInput, type Highlight, Json, type JumpTrace, type KeyResolver, type KeyValueStorage, LogicRule, MAX_CALCULATED_DECIMALS, MAX_FORMULA_ITEMS, MAX_FORMULA_LENGTH, MAX_FORM_FORMULA_LENGTH, MAX_HIDDEN_STRING_LENGTH, MAX_HIGHLIGHT_NOTE_LENGTH, MAX_HIGHLIGHT_QUOTE_LENGTH, MAX_SETTLE_ITEMS, MAX_TABLE_CELL_LENGTH, MAX_TABLE_COLUMNS, MAX_TIMESTAMP_SECONDS, type MaybePromise, type MediaNote, OptionsSource, type OutboxEntry, type OutboxItem, type OutboxSnapshot, PageBreakBlock, type ParseResult, type PathResult, PhotoFormat, PhotoRef, type RuleSpec, type RuleTrace, STRUCTURED_FIELD_TYPES, SubmitFailureReason, type SubmitOutbox, type SubmitOutboxOptions, SubmitRequest, SubmitResult, type TableColumn, calculatedProps, calculatedValue, canonicalJson, carryAnswers, coerceHidden, coerceHiddenValue, computePath, createDraftAutosave, createDraftStore, createSubmitOutbox, defineForm, describeCondition, draftOf, evaluate, evaluateFormula, explain, f, fingerprint, formProgress, formatCalculated, formatTimestamp, formulaInput, formulaNumber, formulaRefs, furthestReached, listLimits, memoryStorage, parseForm, parseFormula, parseTimestamp, recall, renameFormulaKey, restoreDraft, ruleVisible, steps, tableColumns, tableLimits, webStorage, when };
|