@fieldia/core 0.20.0 → 0.22.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 +116 -1
- package/package.json +1 -1
- package/schema/page.schema.json +173 -1
- package/src/index.d.mts +2 -2
- package/src/index.d.ts +2 -2
- package/src/index.js +3 -1
- package/src/index.js.map +1 -1
- package/src/index.mjs +2 -2
- package/src/index.mjs.map +1 -1
- package/src/lib/expression/parser.js +4 -4
- package/src/lib/expression/parser.js.map +1 -1
- package/src/lib/expression/parser.mjs +5 -5
- package/src/lib/expression/parser.mjs.map +1 -1
- package/src/lib/expression/tokenizer.d.mts +12 -0
- package/src/lib/expression/tokenizer.d.ts +12 -0
- package/src/lib/expression/tokenizer.js +21 -12
- package/src/lib/expression/tokenizer.js.map +1 -1
- package/src/lib/expression/tokenizer.mjs +20 -12
- package/src/lib/expression/tokenizer.mjs.map +1 -1
- package/src/lib/format/check-page.d.mts +3 -2
- package/src/lib/format/check-page.d.ts +3 -2
- package/src/lib/format/check-page.js +9 -4
- package/src/lib/format/check-page.js.map +1 -1
- package/src/lib/format/check-page.mjs +10 -5
- package/src/lib/format/check-page.mjs.map +1 -1
- package/src/lib/format/field.d.mts +101 -0
- package/src/lib/format/field.d.ts +101 -0
- package/src/lib/format/field.js +25 -2
- package/src/lib/format/field.js.map +1 -1
- package/src/lib/format/field.mjs +24 -1
- package/src/lib/format/field.mjs.map +1 -1
- package/src/lib/format/layout.d.mts +142 -17
- package/src/lib/format/layout.d.ts +142 -17
- package/src/lib/format/layout.js +18 -1
- package/src/lib/format/layout.js.map +1 -1
- package/src/lib/format/layout.mjs +17 -0
- package/src/lib/format/layout.mjs.map +1 -1
- package/src/lib/format/layout.types.d.mts +6 -2
- package/src/lib/format/layout.types.d.ts +6 -2
- package/src/lib/format/page.d.mts +87 -10
- package/src/lib/format/page.d.ts +87 -10
- package/src/lib/format/page.js +5 -1
- package/src/lib/format/page.js.map +1 -1
- package/src/lib/format/page.mjs +6 -2
- package/src/lib/format/page.mjs.map +1 -1
- package/src/lib/format/pattern.d.mts +10 -0
- package/src/lib/format/pattern.d.ts +10 -0
- package/src/lib/format/pattern.js +33 -0
- package/src/lib/format/pattern.js.map +1 -0
- package/src/lib/format/pattern.mjs +30 -0
- package/src/lib/format/pattern.mjs.map +1 -0
- package/src/lib/format/references.d.mts +7 -0
- package/src/lib/format/references.d.ts +7 -0
- package/src/lib/format/references.js +54 -0
- package/src/lib/format/references.js.map +1 -1
- package/src/lib/format/references.mjs +54 -0
- package/src/lib/format/references.mjs.map +1 -1
- package/src/lib/format/translate.js +6 -4
- package/src/lib/format/translate.js.map +1 -1
- package/src/lib/format/translate.mjs +6 -4
- package/src/lib/format/translate.mjs.map +1 -1
- package/src/lib/format/upgrade.d.mts +41 -0
- package/src/lib/format/upgrade.d.ts +41 -0
- package/src/lib/format/upgrade.js +41 -0
- package/src/lib/format/upgrade.js.map +1 -0
- package/src/lib/format/upgrade.mjs +37 -0
- package/src/lib/format/upgrade.mjs.map +1 -0
- package/src/lib/format/validate.d.mts +2 -1
- package/src/lib/format/validate.d.ts +2 -1
- package/src/lib/format/validate.js +4 -2
- package/src/lib/format/validate.js.map +1 -1
- package/src/lib/format/validate.mjs +4 -2
- package/src/lib/format/validate.mjs.map +1 -1
- package/src/lib/format/version.d.mts +16 -1
- package/src/lib/format/version.d.ts +16 -1
- package/src/lib/format/version.js +39 -2
- package/src/lib/format/version.js.map +1 -1
- package/src/lib/format/version.mjs +36 -1
- package/src/lib/format/version.mjs.map +1 -1
- package/src/lib/record/check.d.mts +13 -3
- package/src/lib/record/check.d.ts +13 -3
- package/src/lib/record/check.js +87 -17
- package/src/lib/record/check.js.map +1 -1
- package/src/lib/record/check.mjs +85 -17
- package/src/lib/record/check.mjs.map +1 -1
- package/src/lib/record/data-source.d.mts +224 -10
- package/src/lib/record/data-source.d.ts +224 -10
- package/src/lib/record/data-source.js +29 -0
- package/src/lib/record/data-source.js.map +1 -1
- package/src/lib/record/data-source.mjs +25 -0
- package/src/lib/record/data-source.mjs.map +1 -1
- package/src/lib/record/filter.js +14 -2
- package/src/lib/record/filter.js.map +1 -1
- package/src/lib/record/filter.mjs +14 -2
- package/src/lib/record/filter.mjs.map +1 -1
- package/src/lib/record/form.d.mts +186 -3
- package/src/lib/record/form.d.ts +186 -3
- package/src/lib/record/form.js +1036 -57
- package/src/lib/record/form.js.map +1 -1
- package/src/lib/record/form.mjs +1037 -60
- package/src/lib/record/form.mjs.map +1 -1
- package/src/lib/record/input-rules.d.mts +277 -0
- package/src/lib/record/input-rules.d.ts +277 -0
- package/src/lib/record/input-rules.js +724 -0
- package/src/lib/record/input-rules.js.map +1 -0
- package/src/lib/record/input-rules.mjs +705 -0
- package/src/lib/record/input-rules.mjs.map +1 -0
- package/src/lib/record/inputs.d.mts +25 -0
- package/src/lib/record/inputs.d.ts +25 -0
- package/src/lib/record/inputs.js +135 -9
- package/src/lib/record/inputs.js.map +1 -1
- package/src/lib/record/inputs.mjs +131 -9
- package/src/lib/record/inputs.mjs.map +1 -1
- package/src/lib/record/locales/ar.js +23 -2
- package/src/lib/record/locales/ar.js.map +1 -1
- package/src/lib/record/locales/ar.mjs +23 -2
- package/src/lib/record/locales/ar.mjs.map +1 -1
- package/src/lib/record/locales/de.js +23 -2
- package/src/lib/record/locales/de.js.map +1 -1
- package/src/lib/record/locales/de.mjs +23 -2
- package/src/lib/record/locales/de.mjs.map +1 -1
- package/src/lib/record/locales/fr.js +23 -2
- package/src/lib/record/locales/fr.js.map +1 -1
- package/src/lib/record/locales/fr.mjs +23 -2
- package/src/lib/record/locales/fr.mjs.map +1 -1
- package/src/lib/record/memory-data-source.d.mts +40 -3
- package/src/lib/record/memory-data-source.d.ts +40 -3
- package/src/lib/record/memory-data-source.js +171 -23
- package/src/lib/record/memory-data-source.js.map +1 -1
- package/src/lib/record/memory-data-source.mjs +171 -23
- package/src/lib/record/memory-data-source.mjs.map +1 -1
- package/src/lib/record/messages.d.mts +43 -0
- package/src/lib/record/messages.d.ts +43 -0
- package/src/lib/record/messages.js +23 -2
- package/src/lib/record/messages.js.map +1 -1
- package/src/lib/record/messages.mjs +23 -2
- package/src/lib/record/messages.mjs.map +1 -1
- package/src/lib/record/rules.js +3 -1
- package/src/lib/record/rules.js.map +1 -1
- package/src/lib/record/rules.mjs +3 -1
- package/src/lib/record/rules.mjs.map +1 -1
- package/src/lib/record/run.d.mts +53 -9
- package/src/lib/record/run.d.ts +53 -9
- package/src/lib/record/run.js +68 -21
- package/src/lib/record/run.js.map +1 -1
- package/src/lib/record/run.mjs +68 -21
- package/src/lib/record/run.mjs.map +1 -1
- package/src/lib/record/search.d.mts +8 -1
- package/src/lib/record/search.d.ts +8 -1
- package/src/lib/record/search.js +28 -8
- package/src/lib/record/search.js.map +1 -1
- package/src/lib/record/search.mjs +28 -8
- package/src/lib/record/search.mjs.map +1 -1
- package/src/lib/record/structures.d.mts +8 -3
- package/src/lib/record/structures.d.ts +8 -3
- package/src/lib/record/structures.js +51 -7
- package/src/lib/record/structures.js.map +1 -1
- package/src/lib/record/structures.mjs +48 -6
- package/src/lib/record/structures.mjs.map +1 -1
- package/src/lib/record/upload-to.d.mts +50 -0
- package/src/lib/record/upload-to.d.ts +50 -0
- package/src/lib/record/upload-to.js +117 -0
- package/src/lib/record/upload-to.js.map +1 -0
- package/src/lib/record/upload-to.mjs +114 -0
- package/src/lib/record/upload-to.mjs.map +1 -0
- package/src/lib/record/uploads.d.mts +152 -0
- package/src/lib/record/uploads.d.ts +152 -0
- package/src/lib/record/uploads.js +345 -0
- package/src/lib/record/uploads.js.map +1 -0
- package/src/lib/record/uploads.mjs +337 -0
- package/src/lib/record/uploads.mjs.map +1 -0
- package/src/lib/record/values.d.mts +6 -0
- package/src/lib/record/values.d.ts +6 -0
- package/src/lib/record/values.js.map +1 -1
- package/src/lib/record/values.mjs.map +1 -1
- package/src/runtime.d.mts +15 -8
- package/src/runtime.d.ts +15 -8
- package/src/runtime.js +48 -1
- package/src/runtime.js.map +1 -1
- package/src/runtime.mjs +12 -5
- package/src/runtime.mjs.map +1 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"upload-to.mjs","sourceRoot":"","sources":["../../../../../../libs/core/src/lib/record/upload-to.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import type { Field, Fields, LineField } from '../format/field.mjs';
|
|
2
|
+
import { type DataSource, type UploadFile } from './data-source.mjs';
|
|
3
|
+
import { type Messages } from './messages.mjs';
|
|
4
|
+
import type { FileValue, RecordId, Value, Values } from './values.mjs';
|
|
5
|
+
/**
|
|
6
|
+
* Files stored as they are picked. Where the data source has `upload`, a
|
|
7
|
+
* file or picture field hands each file to the form, which sends it to the
|
|
8
|
+
* app's store at once — three at a time, the rest waiting their turn, each
|
|
9
|
+
* made ready first when the field asks (a photo made smaller), one at a time
|
|
10
|
+
* — and puts the stored file's link in the field, in the order the files were
|
|
11
|
+
* picked. The form's state shows each on its way; Cancel stops one, Retry
|
|
12
|
+
* sends a failed one again. A save waits for those still going, and stops
|
|
13
|
+
* under its field for one that did not get there. A file still held as its
|
|
14
|
+
* bytes — a signature drawn, a value the app set — is stored by the save.
|
|
15
|
+
*/
|
|
16
|
+
/** A file on its way to the store, as the form's state shows it. */
|
|
17
|
+
export interface FileUpload {
|
|
18
|
+
readonly id: string;
|
|
19
|
+
/** The file or picture field it is for: on a table's line, the line's own field. */
|
|
20
|
+
readonly field: string;
|
|
21
|
+
/** For a field on a line of a table: the table's field and the line's key. */
|
|
22
|
+
readonly line?: {
|
|
23
|
+
readonly field: string;
|
|
24
|
+
readonly key: string;
|
|
25
|
+
};
|
|
26
|
+
readonly name: string;
|
|
27
|
+
readonly size: number;
|
|
28
|
+
readonly type: string;
|
|
29
|
+
/**
|
|
30
|
+
* `waiting` its turn; `preparing`, made ready first (a photo made smaller);
|
|
31
|
+
* `uploading`, `sent` of `total` bytes on their way; `failed`, not stored —
|
|
32
|
+
* Retry may mend it; `refused`, the store will not keep it, `error` says why.
|
|
33
|
+
*/
|
|
34
|
+
readonly status: 'waiting' | 'preparing' | 'uploading' | 'failed' | 'refused';
|
|
35
|
+
readonly sent: number;
|
|
36
|
+
/** The bytes in all, once the store has told them: null, and its bar moves with no number. */
|
|
37
|
+
readonly total: number | null;
|
|
38
|
+
/** Why it failed, or why the store refused it. */
|
|
39
|
+
readonly error?: string;
|
|
40
|
+
}
|
|
41
|
+
/** How files are taken, besides the field they are for. */
|
|
42
|
+
export interface UploadHow {
|
|
43
|
+
/** For a field on a line of a table: the table's field and the line's key. */
|
|
44
|
+
line?: {
|
|
45
|
+
field: string;
|
|
46
|
+
key: string;
|
|
47
|
+
};
|
|
48
|
+
/** Each file made ready before it goes, one at a time: a photo made smaller. A file it cannot make ready goes as it is. */
|
|
49
|
+
prepare?: (file: UploadFile) => Promise<UploadFile>;
|
|
50
|
+
}
|
|
51
|
+
/** How many files go to the store at once; the rest wait their turn. */
|
|
52
|
+
export declare const UPLOADS_AT_ONCE = 3;
|
|
53
|
+
/** Whether an upload is still going: waiting, being made ready, or on its way. */
|
|
54
|
+
export declare const uploadGoing: (upload: FileUpload) => boolean;
|
|
55
|
+
/** What the form gives the uploads: where files go, where their links are put, and how the state is told. */
|
|
56
|
+
export interface UploadsHost {
|
|
57
|
+
source(): DataSource | undefined;
|
|
58
|
+
/** The page's model, or a page of responses' id; for a saved form placed in a record, the record's. */
|
|
59
|
+
model(): string;
|
|
60
|
+
recordId(): RecordId | null;
|
|
61
|
+
/**
|
|
62
|
+
* Where the form's fields sit in the record its files are for, before each
|
|
63
|
+
* field's name: a saved form placed in a record keeps its answers under the
|
|
64
|
+
* part's name, so its field `doc` is `consent.doc` there. Empty otherwise.
|
|
65
|
+
*/
|
|
66
|
+
under?: string;
|
|
67
|
+
/** A line's id, once it is saved. */
|
|
68
|
+
lineId(field: string, key: string): RecordId | undefined;
|
|
69
|
+
/** A field's or a line field's definition, or null when it holds no files. */
|
|
70
|
+
fileField(field: string, line?: {
|
|
71
|
+
field: string;
|
|
72
|
+
key: string;
|
|
73
|
+
}): Field | LineField | null;
|
|
74
|
+
/** The field's value now. */
|
|
75
|
+
read(field: string, line?: {
|
|
76
|
+
field: string;
|
|
77
|
+
key: string;
|
|
78
|
+
}): Value | undefined;
|
|
79
|
+
/** Write the field: a person's pick, or (`quiet`) a save putting a link in place of the bytes. */
|
|
80
|
+
write(field: string, line: {
|
|
81
|
+
field: string;
|
|
82
|
+
key: string;
|
|
83
|
+
} | undefined, value: Value, quiet: boolean): void;
|
|
84
|
+
changed(uploads: readonly FileUpload[]): void;
|
|
85
|
+
track<T>(promise: Promise<T>): Promise<T>;
|
|
86
|
+
}
|
|
87
|
+
interface Entry {
|
|
88
|
+
id: string;
|
|
89
|
+
/** Its place in the order files were picked. */
|
|
90
|
+
seq: number;
|
|
91
|
+
field: string;
|
|
92
|
+
line?: {
|
|
93
|
+
field: string;
|
|
94
|
+
key: string;
|
|
95
|
+
};
|
|
96
|
+
file: UploadFile;
|
|
97
|
+
prepare?: (file: UploadFile) => Promise<UploadFile>;
|
|
98
|
+
/** Made ready, or needing nothing done. */
|
|
99
|
+
ready: boolean;
|
|
100
|
+
/** The file held as its bytes that this one is stored for: a save puts the link in its place. */
|
|
101
|
+
replaces?: FileValue;
|
|
102
|
+
status: FileUpload['status'];
|
|
103
|
+
sent: number;
|
|
104
|
+
total: number | null;
|
|
105
|
+
error?: string;
|
|
106
|
+
stop?: () => void;
|
|
107
|
+
/** Ends its run: stored, failed, refused or cancelled. */
|
|
108
|
+
end?: () => void;
|
|
109
|
+
}
|
|
110
|
+
/** A file held as its bytes, as the store takes a file: its name, size, kind and bytes. */
|
|
111
|
+
export declare function fileFromBytes(file: FileValue): UploadFile;
|
|
112
|
+
/**
|
|
113
|
+
* The values as a draft keeps them: a file held as its bytes is left out —
|
|
114
|
+
* a field holding one is not kept at all, and a line's loses that file —
|
|
115
|
+
* so a draft stays small enough for the browser's store, and restoring it
|
|
116
|
+
* leaves those fields as the record holds them.
|
|
117
|
+
*/
|
|
118
|
+
export declare function withoutFileBytes(fields: Fields, values: Readonly<Values>): Values;
|
|
119
|
+
/** Each file still held as its bytes in the fields that changed — on the record, or on a changed table's lines — for a save to store. */
|
|
120
|
+
export declare function fileBytesIn(fields: Fields, values: Readonly<Values>, changed: readonly string[]): {
|
|
121
|
+
field: string;
|
|
122
|
+
line?: {
|
|
123
|
+
field: string;
|
|
124
|
+
key: string;
|
|
125
|
+
};
|
|
126
|
+
file: FileValue;
|
|
127
|
+
}[];
|
|
128
|
+
/** The uploads of one form. */
|
|
129
|
+
export declare function createUploads(host: UploadsHost): {
|
|
130
|
+
/** Send files picked for a field: its ids. */
|
|
131
|
+
start(field: string, files: readonly UploadFile[], how?: {
|
|
132
|
+
line?: Entry["line"];
|
|
133
|
+
prepare?: Entry["prepare"];
|
|
134
|
+
}): string[];
|
|
135
|
+
cancel(id: string): void;
|
|
136
|
+
retry(id: string): void;
|
|
137
|
+
/** Stop every upload: the changes they were for are gone. */
|
|
138
|
+
clear(): void;
|
|
139
|
+
/** Whether any file is still going. */
|
|
140
|
+
going: () => boolean;
|
|
141
|
+
settled: () => Promise<void>;
|
|
142
|
+
/** Store each file still held as its bytes in the fields given, and wait for them all. */
|
|
143
|
+
storeBytes(found: {
|
|
144
|
+
field: string;
|
|
145
|
+
line?: Entry["line"];
|
|
146
|
+
file: FileValue;
|
|
147
|
+
}[]): Promise<void>;
|
|
148
|
+
/** What stops a save, under each field: a file that failed, or that the store refused. */
|
|
149
|
+
problems(messages: Messages): Record<string, string>;
|
|
150
|
+
};
|
|
151
|
+
export type Uploads = ReturnType<typeof createUploads>;
|
|
152
|
+
export {};
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import type { Field, Fields, LineField } from '../format/field';
|
|
2
|
+
import { type DataSource, type UploadFile } from './data-source';
|
|
3
|
+
import { type Messages } from './messages';
|
|
4
|
+
import type { FileValue, RecordId, Value, Values } from './values';
|
|
5
|
+
/**
|
|
6
|
+
* Files stored as they are picked. Where the data source has `upload`, a
|
|
7
|
+
* file or picture field hands each file to the form, which sends it to the
|
|
8
|
+
* app's store at once — three at a time, the rest waiting their turn, each
|
|
9
|
+
* made ready first when the field asks (a photo made smaller), one at a time
|
|
10
|
+
* — and puts the stored file's link in the field, in the order the files were
|
|
11
|
+
* picked. The form's state shows each on its way; Cancel stops one, Retry
|
|
12
|
+
* sends a failed one again. A save waits for those still going, and stops
|
|
13
|
+
* under its field for one that did not get there. A file still held as its
|
|
14
|
+
* bytes — a signature drawn, a value the app set — is stored by the save.
|
|
15
|
+
*/
|
|
16
|
+
/** A file on its way to the store, as the form's state shows it. */
|
|
17
|
+
export interface FileUpload {
|
|
18
|
+
readonly id: string;
|
|
19
|
+
/** The file or picture field it is for: on a table's line, the line's own field. */
|
|
20
|
+
readonly field: string;
|
|
21
|
+
/** For a field on a line of a table: the table's field and the line's key. */
|
|
22
|
+
readonly line?: {
|
|
23
|
+
readonly field: string;
|
|
24
|
+
readonly key: string;
|
|
25
|
+
};
|
|
26
|
+
readonly name: string;
|
|
27
|
+
readonly size: number;
|
|
28
|
+
readonly type: string;
|
|
29
|
+
/**
|
|
30
|
+
* `waiting` its turn; `preparing`, made ready first (a photo made smaller);
|
|
31
|
+
* `uploading`, `sent` of `total` bytes on their way; `failed`, not stored —
|
|
32
|
+
* Retry may mend it; `refused`, the store will not keep it, `error` says why.
|
|
33
|
+
*/
|
|
34
|
+
readonly status: 'waiting' | 'preparing' | 'uploading' | 'failed' | 'refused';
|
|
35
|
+
readonly sent: number;
|
|
36
|
+
/** The bytes in all, once the store has told them: null, and its bar moves with no number. */
|
|
37
|
+
readonly total: number | null;
|
|
38
|
+
/** Why it failed, or why the store refused it. */
|
|
39
|
+
readonly error?: string;
|
|
40
|
+
}
|
|
41
|
+
/** How files are taken, besides the field they are for. */
|
|
42
|
+
export interface UploadHow {
|
|
43
|
+
/** For a field on a line of a table: the table's field and the line's key. */
|
|
44
|
+
line?: {
|
|
45
|
+
field: string;
|
|
46
|
+
key: string;
|
|
47
|
+
};
|
|
48
|
+
/** Each file made ready before it goes, one at a time: a photo made smaller. A file it cannot make ready goes as it is. */
|
|
49
|
+
prepare?: (file: UploadFile) => Promise<UploadFile>;
|
|
50
|
+
}
|
|
51
|
+
/** How many files go to the store at once; the rest wait their turn. */
|
|
52
|
+
export declare const UPLOADS_AT_ONCE = 3;
|
|
53
|
+
/** Whether an upload is still going: waiting, being made ready, or on its way. */
|
|
54
|
+
export declare const uploadGoing: (upload: FileUpload) => boolean;
|
|
55
|
+
/** What the form gives the uploads: where files go, where their links are put, and how the state is told. */
|
|
56
|
+
export interface UploadsHost {
|
|
57
|
+
source(): DataSource | undefined;
|
|
58
|
+
/** The page's model, or a page of responses' id; for a saved form placed in a record, the record's. */
|
|
59
|
+
model(): string;
|
|
60
|
+
recordId(): RecordId | null;
|
|
61
|
+
/**
|
|
62
|
+
* Where the form's fields sit in the record its files are for, before each
|
|
63
|
+
* field's name: a saved form placed in a record keeps its answers under the
|
|
64
|
+
* part's name, so its field `doc` is `consent.doc` there. Empty otherwise.
|
|
65
|
+
*/
|
|
66
|
+
under?: string;
|
|
67
|
+
/** A line's id, once it is saved. */
|
|
68
|
+
lineId(field: string, key: string): RecordId | undefined;
|
|
69
|
+
/** A field's or a line field's definition, or null when it holds no files. */
|
|
70
|
+
fileField(field: string, line?: {
|
|
71
|
+
field: string;
|
|
72
|
+
key: string;
|
|
73
|
+
}): Field | LineField | null;
|
|
74
|
+
/** The field's value now. */
|
|
75
|
+
read(field: string, line?: {
|
|
76
|
+
field: string;
|
|
77
|
+
key: string;
|
|
78
|
+
}): Value | undefined;
|
|
79
|
+
/** Write the field: a person's pick, or (`quiet`) a save putting a link in place of the bytes. */
|
|
80
|
+
write(field: string, line: {
|
|
81
|
+
field: string;
|
|
82
|
+
key: string;
|
|
83
|
+
} | undefined, value: Value, quiet: boolean): void;
|
|
84
|
+
changed(uploads: readonly FileUpload[]): void;
|
|
85
|
+
track<T>(promise: Promise<T>): Promise<T>;
|
|
86
|
+
}
|
|
87
|
+
interface Entry {
|
|
88
|
+
id: string;
|
|
89
|
+
/** Its place in the order files were picked. */
|
|
90
|
+
seq: number;
|
|
91
|
+
field: string;
|
|
92
|
+
line?: {
|
|
93
|
+
field: string;
|
|
94
|
+
key: string;
|
|
95
|
+
};
|
|
96
|
+
file: UploadFile;
|
|
97
|
+
prepare?: (file: UploadFile) => Promise<UploadFile>;
|
|
98
|
+
/** Made ready, or needing nothing done. */
|
|
99
|
+
ready: boolean;
|
|
100
|
+
/** The file held as its bytes that this one is stored for: a save puts the link in its place. */
|
|
101
|
+
replaces?: FileValue;
|
|
102
|
+
status: FileUpload['status'];
|
|
103
|
+
sent: number;
|
|
104
|
+
total: number | null;
|
|
105
|
+
error?: string;
|
|
106
|
+
stop?: () => void;
|
|
107
|
+
/** Ends its run: stored, failed, refused or cancelled. */
|
|
108
|
+
end?: () => void;
|
|
109
|
+
}
|
|
110
|
+
/** A file held as its bytes, as the store takes a file: its name, size, kind and bytes. */
|
|
111
|
+
export declare function fileFromBytes(file: FileValue): UploadFile;
|
|
112
|
+
/**
|
|
113
|
+
* The values as a draft keeps them: a file held as its bytes is left out —
|
|
114
|
+
* a field holding one is not kept at all, and a line's loses that file —
|
|
115
|
+
* so a draft stays small enough for the browser's store, and restoring it
|
|
116
|
+
* leaves those fields as the record holds them.
|
|
117
|
+
*/
|
|
118
|
+
export declare function withoutFileBytes(fields: Fields, values: Readonly<Values>): Values;
|
|
119
|
+
/** Each file still held as its bytes in the fields that changed — on the record, or on a changed table's lines — for a save to store. */
|
|
120
|
+
export declare function fileBytesIn(fields: Fields, values: Readonly<Values>, changed: readonly string[]): {
|
|
121
|
+
field: string;
|
|
122
|
+
line?: {
|
|
123
|
+
field: string;
|
|
124
|
+
key: string;
|
|
125
|
+
};
|
|
126
|
+
file: FileValue;
|
|
127
|
+
}[];
|
|
128
|
+
/** The uploads of one form. */
|
|
129
|
+
export declare function createUploads(host: UploadsHost): {
|
|
130
|
+
/** Send files picked for a field: its ids. */
|
|
131
|
+
start(field: string, files: readonly UploadFile[], how?: {
|
|
132
|
+
line?: Entry["line"];
|
|
133
|
+
prepare?: Entry["prepare"];
|
|
134
|
+
}): string[];
|
|
135
|
+
cancel(id: string): void;
|
|
136
|
+
retry(id: string): void;
|
|
137
|
+
/** Stop every upload: the changes they were for are gone. */
|
|
138
|
+
clear(): void;
|
|
139
|
+
/** Whether any file is still going. */
|
|
140
|
+
going: () => boolean;
|
|
141
|
+
settled: () => Promise<void>;
|
|
142
|
+
/** Store each file still held as its bytes in the fields given, and wait for them all. */
|
|
143
|
+
storeBytes(found: {
|
|
144
|
+
field: string;
|
|
145
|
+
line?: Entry["line"];
|
|
146
|
+
file: FileValue;
|
|
147
|
+
}[]): Promise<void>;
|
|
148
|
+
/** What stops a save, under each field: a file that failed, or that the store refused. */
|
|
149
|
+
problems(messages: Messages): Record<string, string>;
|
|
150
|
+
};
|
|
151
|
+
export type Uploads = ReturnType<typeof createUploads>;
|
|
152
|
+
export {};
|
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.uploadGoing = exports.UPLOADS_AT_ONCE = void 0;
|
|
4
|
+
exports.fileFromBytes = fileFromBytes;
|
|
5
|
+
exports.withoutFileBytes = withoutFileBytes;
|
|
6
|
+
exports.fileBytesIn = fileBytesIn;
|
|
7
|
+
exports.createUploads = createUploads;
|
|
8
|
+
const data_source_1 = require("./data-source");
|
|
9
|
+
const messages_1 = require("./messages");
|
|
10
|
+
/** How many files go to the store at once; the rest wait their turn. */
|
|
11
|
+
exports.UPLOADS_AT_ONCE = 3;
|
|
12
|
+
const going = (status) => status !== 'failed' && status !== 'refused';
|
|
13
|
+
/** Whether an upload is still going: waiting, being made ready, or on its way. */
|
|
14
|
+
const uploadGoing = (upload) => going(upload.status);
|
|
15
|
+
exports.uploadGoing = uploadGoing;
|
|
16
|
+
/** The host's `AbortController`: every browser's, and Node's. Core names no DOM types. */
|
|
17
|
+
const stopper = () => new globalThis.AbortController();
|
|
18
|
+
const sameFile = (a, b) => a.name === b.name && a.data === b.data && a.url === b.url;
|
|
19
|
+
const isFile = (value) => !!value && typeof value === 'object' && typeof value.name === 'string' && typeof value.size === 'number';
|
|
20
|
+
const filesIn = (value) => (Array.isArray(value) ? value.filter(isFile) : isFile(value) ? [value] : []);
|
|
21
|
+
const holdsFiles = (def) => def?.type === 'binary' || def?.type === 'image';
|
|
22
|
+
/** The bytes a base64 text holds. */
|
|
23
|
+
function bytesOf(data) {
|
|
24
|
+
const text = globalThis.atob(data);
|
|
25
|
+
const bytes = new Uint8Array(text.length);
|
|
26
|
+
for (let i = 0; i < text.length; i++)
|
|
27
|
+
bytes[i] = text.charCodeAt(i);
|
|
28
|
+
return bytes;
|
|
29
|
+
}
|
|
30
|
+
/** A file held as its bytes, as the store takes a file: its name, size, kind and bytes. */
|
|
31
|
+
function fileFromBytes(file) {
|
|
32
|
+
const bytes = bytesOf(file.data ?? '');
|
|
33
|
+
return { name: file.name, size: bytes.length, type: file.type, arrayBuffer: async () => bytes.slice().buffer };
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* The values as a draft keeps them: a file held as its bytes is left out —
|
|
37
|
+
* a field holding one is not kept at all, and a line's loses that file —
|
|
38
|
+
* so a draft stays small enough for the browser's store, and restoring it
|
|
39
|
+
* leaves those fields as the record holds them.
|
|
40
|
+
*/
|
|
41
|
+
function withoutFileBytes(fields, values) {
|
|
42
|
+
const kept = {};
|
|
43
|
+
for (const [name, value] of Object.entries(values)) {
|
|
44
|
+
const def = fields[name];
|
|
45
|
+
if (holdsFiles(def)) {
|
|
46
|
+
if (!filesIn(value).some((file) => file.data !== undefined))
|
|
47
|
+
kept[name] = value;
|
|
48
|
+
}
|
|
49
|
+
else if (def?.type === 'one2many' && Array.isArray(value)) {
|
|
50
|
+
const subs = Object.entries(def.fields).filter(([, sub]) => holdsFiles(sub)).map(([sub]) => sub);
|
|
51
|
+
kept[name] = !subs.length
|
|
52
|
+
? value
|
|
53
|
+
: value.map((line) => {
|
|
54
|
+
const own = { ...line.values };
|
|
55
|
+
for (const sub of subs)
|
|
56
|
+
if (filesIn(own[sub]).some((file) => file.data !== undefined))
|
|
57
|
+
delete own[sub];
|
|
58
|
+
return { ...line, values: own };
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
else
|
|
62
|
+
kept[name] = value;
|
|
63
|
+
}
|
|
64
|
+
return kept;
|
|
65
|
+
}
|
|
66
|
+
/** Each file still held as its bytes in the fields that changed — on the record, or on a changed table's lines — for a save to store. */
|
|
67
|
+
function fileBytesIn(fields, values, changed) {
|
|
68
|
+
const found = [];
|
|
69
|
+
for (const name of changed) {
|
|
70
|
+
const def = fields[name];
|
|
71
|
+
if (holdsFiles(def))
|
|
72
|
+
for (const file of filesIn(values[name]))
|
|
73
|
+
if (file.data !== undefined)
|
|
74
|
+
found.push({ field: name, file });
|
|
75
|
+
if (def?.type !== 'one2many')
|
|
76
|
+
continue;
|
|
77
|
+
for (const line of values[name] ?? []) {
|
|
78
|
+
for (const [sub, subDef] of Object.entries(def.fields)) {
|
|
79
|
+
if (holdsFiles(subDef))
|
|
80
|
+
for (const file of filesIn(line.values[sub]))
|
|
81
|
+
if (file.data !== undefined)
|
|
82
|
+
found.push({ field: sub, line: { field: name, key: line.key }, file });
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return found;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* A field's files with a stored one put in: in place of the bytes it was
|
|
90
|
+
* stored for (nothing, when those are gone), in place of a field's one file,
|
|
91
|
+
* or among several before the first that was picked after it.
|
|
92
|
+
*/
|
|
93
|
+
function placed(current, stored, how) {
|
|
94
|
+
if (how.replaces) {
|
|
95
|
+
const was = how.replaces;
|
|
96
|
+
if (Array.isArray(current)) {
|
|
97
|
+
const at = current.findIndex((file) => isFile(file) && sameFile(file, was));
|
|
98
|
+
return at < 0 ? undefined : current.map((file, i) => (i === at ? stored : file));
|
|
99
|
+
}
|
|
100
|
+
return isFile(current) && sameFile(current, was) ? stored : undefined;
|
|
101
|
+
}
|
|
102
|
+
if (!how.several)
|
|
103
|
+
return stored;
|
|
104
|
+
const files = filesIn(current);
|
|
105
|
+
const at = files.findIndex(how.pickedAfter);
|
|
106
|
+
const list = at < 0 ? [...files, stored] : [...files.slice(0, at), stored, ...files.slice(at)];
|
|
107
|
+
return list;
|
|
108
|
+
}
|
|
109
|
+
/** The uploads of one form. */
|
|
110
|
+
function createUploads(host) {
|
|
111
|
+
let entries = [];
|
|
112
|
+
let made = 0;
|
|
113
|
+
let picked = 0;
|
|
114
|
+
/** Where each stored file came in the order picked, by its link: a later one goes after it. */
|
|
115
|
+
const order = new Map();
|
|
116
|
+
const waiters = new Set();
|
|
117
|
+
const shown = (entry) => ({
|
|
118
|
+
id: entry.id,
|
|
119
|
+
field: entry.field,
|
|
120
|
+
...(entry.line ? { line: entry.line } : {}),
|
|
121
|
+
name: entry.file.name,
|
|
122
|
+
size: entry.file.size,
|
|
123
|
+
type: entry.file.type,
|
|
124
|
+
status: entry.status,
|
|
125
|
+
sent: entry.sent,
|
|
126
|
+
total: entry.total,
|
|
127
|
+
...(entry.error !== undefined ? { error: entry.error } : {}),
|
|
128
|
+
});
|
|
129
|
+
function tell() {
|
|
130
|
+
host.changed(entries.map(shown));
|
|
131
|
+
if (!entries.some((entry) => going(entry.status)))
|
|
132
|
+
for (const done of [...waiters])
|
|
133
|
+
done();
|
|
134
|
+
}
|
|
135
|
+
function drop(entry) {
|
|
136
|
+
entries = entries.filter((other) => other !== entry);
|
|
137
|
+
entry.stop?.();
|
|
138
|
+
entry.end?.();
|
|
139
|
+
}
|
|
140
|
+
/** The next to be made ready, one at a time, and the next to go, three at a time. */
|
|
141
|
+
function pump() {
|
|
142
|
+
if (!entries.some((entry) => entry.status === 'preparing')) {
|
|
143
|
+
const next = entries.find((entry) => entry.status === 'waiting' && !entry.ready);
|
|
144
|
+
if (next)
|
|
145
|
+
void prepare(next);
|
|
146
|
+
}
|
|
147
|
+
for (let next = nextToSend(); next && entries.filter((entry) => entry.status === 'uploading').length < exports.UPLOADS_AT_ONCE; next = nextToSend())
|
|
148
|
+
void send(next);
|
|
149
|
+
}
|
|
150
|
+
const nextToSend = () => entries.find((entry) => entry.status === 'waiting' && entry.ready);
|
|
151
|
+
async function prepare(entry) {
|
|
152
|
+
entry.status = 'preparing';
|
|
153
|
+
tell();
|
|
154
|
+
const original = entry.file;
|
|
155
|
+
let refused = null;
|
|
156
|
+
try {
|
|
157
|
+
entry.file = await entry.prepare(original);
|
|
158
|
+
}
|
|
159
|
+
catch (error) {
|
|
160
|
+
// A file that cannot be made ready goes as it is, never lost — unless the field refuses it as it is (too large still).
|
|
161
|
+
const problem = (0, data_source_1.saveProblemOf)(error);
|
|
162
|
+
if (problem.kind === 'rule')
|
|
163
|
+
refused = problem.message;
|
|
164
|
+
entry.file = original;
|
|
165
|
+
}
|
|
166
|
+
entry.ready = true;
|
|
167
|
+
if (!entries.includes(entry))
|
|
168
|
+
return;
|
|
169
|
+
entry.status = refused === null ? 'waiting' : 'refused';
|
|
170
|
+
if (refused !== null) {
|
|
171
|
+
entry.error = refused;
|
|
172
|
+
entry.end?.();
|
|
173
|
+
}
|
|
174
|
+
tell();
|
|
175
|
+
pump();
|
|
176
|
+
}
|
|
177
|
+
async function send(entry) {
|
|
178
|
+
const source = host.source();
|
|
179
|
+
if (!source?.upload)
|
|
180
|
+
return;
|
|
181
|
+
const stop = stopper();
|
|
182
|
+
entry.status = 'uploading';
|
|
183
|
+
entry.sent = 0;
|
|
184
|
+
entry.total = null;
|
|
185
|
+
delete entry.error;
|
|
186
|
+
entry.stop = () => stop.abort();
|
|
187
|
+
tell();
|
|
188
|
+
const lineId = entry.line ? host.lineId(entry.line.field, entry.line.key) : undefined;
|
|
189
|
+
const under = host.under ?? '';
|
|
190
|
+
const request = {
|
|
191
|
+
model: host.model(),
|
|
192
|
+
// A line's field is named on its own, its table where it sits.
|
|
193
|
+
field: entry.line ? entry.field : `${under}${entry.field}`,
|
|
194
|
+
recordId: host.recordId(),
|
|
195
|
+
...(entry.line ? { line: { ...entry.line, field: `${under}${entry.line.field}`, ...(lineId !== undefined ? { id: lineId } : {}) } } : {}),
|
|
196
|
+
signal: stop.signal,
|
|
197
|
+
onProgress: (sent, total) => {
|
|
198
|
+
if (entry.status !== 'uploading' || !entries.includes(entry))
|
|
199
|
+
return;
|
|
200
|
+
// Told when the bar would move: a whole per cent, or the end.
|
|
201
|
+
const was = entry.total ? Math.floor((entry.sent * 100) / entry.total) : -1;
|
|
202
|
+
const now = total ? Math.floor((sent * 100) / total) : -1;
|
|
203
|
+
entry.sent = sent;
|
|
204
|
+
entry.total = total;
|
|
205
|
+
if (now !== was || sent === total)
|
|
206
|
+
tell();
|
|
207
|
+
},
|
|
208
|
+
};
|
|
209
|
+
try {
|
|
210
|
+
const stored = await source.upload(entry.file, request);
|
|
211
|
+
if (!entries.includes(entry))
|
|
212
|
+
return;
|
|
213
|
+
entries = entries.filter((other) => other !== entry);
|
|
214
|
+
put(entry, stored);
|
|
215
|
+
entry.end?.();
|
|
216
|
+
}
|
|
217
|
+
catch (error) {
|
|
218
|
+
if (!entries.includes(entry) || stop.signal.aborted || error?.name === 'AbortError') {
|
|
219
|
+
if (entries.includes(entry))
|
|
220
|
+
drop(entry);
|
|
221
|
+
}
|
|
222
|
+
else {
|
|
223
|
+
const problem = (0, data_source_1.saveProblemOf)(error);
|
|
224
|
+
entry.status = problem.kind === 'rule' ? 'refused' : 'failed';
|
|
225
|
+
entry.error = problem.message;
|
|
226
|
+
entry.end?.();
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
finally {
|
|
230
|
+
delete entry.stop;
|
|
231
|
+
tell();
|
|
232
|
+
pump();
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
/** The stored file in its field: exactly what the store gave — its other words kept, for bytes it stands in for. */
|
|
236
|
+
function put(entry, stored) {
|
|
237
|
+
const def = host.fileField(entry.field, entry.line);
|
|
238
|
+
const extra = {};
|
|
239
|
+
if (entry.replaces)
|
|
240
|
+
for (const [key, value] of Object.entries(entry.replaces))
|
|
241
|
+
if (!['data', 'url', 'name', 'size', 'type', 'fileId'].includes(key))
|
|
242
|
+
extra[key] = value;
|
|
243
|
+
const file = { ...stored, ...extra };
|
|
244
|
+
order.set(file.url ?? '', entry.seq);
|
|
245
|
+
const value = placed(host.read(entry.field, entry.line), file, {
|
|
246
|
+
several: def?.multiple === true,
|
|
247
|
+
...(entry.replaces ? { replaces: entry.replaces } : {}),
|
|
248
|
+
pickedAfter: (other) => (order.get(other.url ?? '') ?? -1) > entry.seq,
|
|
249
|
+
});
|
|
250
|
+
if (value !== undefined)
|
|
251
|
+
host.write(entry.field, entry.line, value, !!entry.replaces);
|
|
252
|
+
}
|
|
253
|
+
function begin(field, line, file, more) {
|
|
254
|
+
const entry = { id: `upload-${++made}`, seq: ++picked, field, ...(line ? { line } : {}), file, ...(more.prepare ? { prepare: more.prepare } : {}), ready: !more.prepare, ...(more.replaces ? { replaces: more.replaces } : {}), status: 'waiting', sent: 0, total: null };
|
|
255
|
+
entries = [...entries, entry];
|
|
256
|
+
void host.track(new Promise((resolve) => (entry.end = resolve)));
|
|
257
|
+
return entry;
|
|
258
|
+
}
|
|
259
|
+
/** Wait until no file is still going: each one stored, failed, refused or cancelled. */
|
|
260
|
+
const settled = () => new Promise((resolve) => {
|
|
261
|
+
if (!entries.some((entry) => going(entry.status)))
|
|
262
|
+
return resolve();
|
|
263
|
+
const done = () => (waiters.delete(done), resolve());
|
|
264
|
+
waiters.add(done);
|
|
265
|
+
});
|
|
266
|
+
return {
|
|
267
|
+
/** Send files picked for a field: its ids. */
|
|
268
|
+
start(field, files, how = {}) {
|
|
269
|
+
if (!host.source()?.upload)
|
|
270
|
+
throw new Error('The data source has no upload: files go inside the record');
|
|
271
|
+
const def = host.fileField(field, how.line);
|
|
272
|
+
if (!def)
|
|
273
|
+
throw new Error(how.line ? `The lines of "${how.line.field}" have no file field "${field}"` : `"${field}" is not a file or picture field`);
|
|
274
|
+
// A field of one file: the last one picked is the one it keeps; one still on its way is stopped.
|
|
275
|
+
if (def.multiple !== true) {
|
|
276
|
+
for (const entry of entries.filter((e) => e.field === field && e.line?.key === how.line?.key && e.line?.field === how.line?.field && !e.replaces))
|
|
277
|
+
drop(entry);
|
|
278
|
+
files = files.slice(-1);
|
|
279
|
+
}
|
|
280
|
+
const started = files.map((file) => begin(field, how.line, file, { prepare: how.prepare }));
|
|
281
|
+
tell();
|
|
282
|
+
pump();
|
|
283
|
+
return started.map((entry) => entry.id);
|
|
284
|
+
},
|
|
285
|
+
cancel(id) {
|
|
286
|
+
const entry = entries.find((e) => e.id === id);
|
|
287
|
+
if (!entry)
|
|
288
|
+
return;
|
|
289
|
+
drop(entry);
|
|
290
|
+
tell();
|
|
291
|
+
pump();
|
|
292
|
+
},
|
|
293
|
+
retry(id) {
|
|
294
|
+
const entry = entries.find((e) => e.id === id);
|
|
295
|
+
if (!entry || entry.status !== 'failed')
|
|
296
|
+
return;
|
|
297
|
+
entry.status = 'waiting';
|
|
298
|
+
entry.sent = 0;
|
|
299
|
+
entry.total = null;
|
|
300
|
+
delete entry.error;
|
|
301
|
+
void host.track(new Promise((resolve) => (entry.end = resolve)));
|
|
302
|
+
tell();
|
|
303
|
+
pump();
|
|
304
|
+
},
|
|
305
|
+
/** Stop every upload: the changes they were for are gone. */
|
|
306
|
+
clear() {
|
|
307
|
+
if (!entries.length)
|
|
308
|
+
return;
|
|
309
|
+
for (const entry of [...entries])
|
|
310
|
+
drop(entry);
|
|
311
|
+
tell();
|
|
312
|
+
},
|
|
313
|
+
/** Whether any file is still going. */
|
|
314
|
+
going: () => entries.some((entry) => going(entry.status)),
|
|
315
|
+
settled,
|
|
316
|
+
/** Store each file still held as its bytes in the fields given, and wait for them all. */
|
|
317
|
+
async storeBytes(found) {
|
|
318
|
+
if (!found.length || !host.source()?.upload)
|
|
319
|
+
return;
|
|
320
|
+
for (const { field, line, file } of found)
|
|
321
|
+
begin(field, line, fileFromBytes(file), { replaces: file });
|
|
322
|
+
tell();
|
|
323
|
+
pump();
|
|
324
|
+
await settled();
|
|
325
|
+
},
|
|
326
|
+
/** What stops a save, under each field: a file that failed, or that the store refused. */
|
|
327
|
+
problems(messages) {
|
|
328
|
+
const out = {};
|
|
329
|
+
const by = new Map();
|
|
330
|
+
for (const entry of entries.filter((e) => !going(e.status))) {
|
|
331
|
+
const key = entry.line ? `${entry.line.field}.${entry.line.key}.${entry.field}` : entry.field;
|
|
332
|
+
by.set(key, [...(by.get(key) ?? []), entry]);
|
|
333
|
+
}
|
|
334
|
+
for (const [key, list] of by) {
|
|
335
|
+
const refused = list.filter((entry) => entry.status === 'refused');
|
|
336
|
+
out[key] =
|
|
337
|
+
list.length > 1 ? (0, messages_1.fill)(messages.notUploadedMany, { n: list.length })
|
|
338
|
+
: refused.length ? (0, messages_1.fill)(messages.fileRefused, { name: list[0].file.name, reason: list[0].error ?? '' })
|
|
339
|
+
: (0, messages_1.fill)(messages.notUploaded, { name: list[0].file.name });
|
|
340
|
+
}
|
|
341
|
+
return out;
|
|
342
|
+
},
|
|
343
|
+
};
|
|
344
|
+
}
|
|
345
|
+
//# sourceMappingURL=uploads.js.map
|