@brydio/manifest 0.1.0-alpha.34 → 0.1.0-alpha.35
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/package.json +2 -2
- package/src/confirm-email.d.ts +40 -0
- package/src/confirm-email.js +78 -0
- package/src/index.d.ts +1 -0
- package/src/index.js +1 -0
- package/src/schema.d.ts +32 -1
- package/src/schema.js +12 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@brydio/manifest",
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
3
|
+
"version": "0.1.0-alpha.35",
|
|
4
4
|
"description": "The shape of a Brydio app's manifest, its collections' schemas, and the checks that say what is wrong with one.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"url": "https://github.com/nakel-ola/brydio-sdk",
|
|
11
11
|
"directory": "packages/manifest"
|
|
12
12
|
},
|
|
13
|
-
"gitHead": "
|
|
13
|
+
"gitHead": "5bd1e984602ccea955d5755e613fe773ae4899ca",
|
|
14
14
|
"exports": {
|
|
15
15
|
".": {
|
|
16
16
|
"types": "./src/index.d.ts",
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
/**
|
|
3
|
+
* The confirmation email of a public form (FO04). Mirrors Brydio's
|
|
4
|
+
* `apps/api/src/apps/manifest/confirm-email.ts`; the two change together.
|
|
5
|
+
*
|
|
6
|
+
* An app only declares it: which field of a `publicSubmit` collection holds
|
|
7
|
+
* the visitor's address, and a short plain text to say. Brydio decides the
|
|
8
|
+
* rest — when it is sent, to whom, how often, from whom — so an anonymous
|
|
9
|
+
* page can never be used to send anybody anything else.
|
|
10
|
+
*/
|
|
11
|
+
export declare const CONFIRM_LIMITS: {
|
|
12
|
+
readonly subjectChars: 120;
|
|
13
|
+
readonly messageChars: 600;
|
|
14
|
+
};
|
|
15
|
+
export declare const confirmEmailSchema: z.ZodObject<{
|
|
16
|
+
field: z.ZodString;
|
|
17
|
+
subject: z.ZodOptional<z.ZodString>;
|
|
18
|
+
message: z.ZodOptional<z.ZodString>;
|
|
19
|
+
link: z.ZodOptional<z.ZodBoolean>;
|
|
20
|
+
}, z.core.$strict>;
|
|
21
|
+
export type ConfirmEmailDeclared = z.infer<typeof confirmEmailSchema>;
|
|
22
|
+
/** As the host reads it, on `CollectionSpec.confirmEmail`. */
|
|
23
|
+
export interface ConfirmEmailSpec {
|
|
24
|
+
field: string;
|
|
25
|
+
subject: string | null;
|
|
26
|
+
message: string | null;
|
|
27
|
+
link: boolean;
|
|
28
|
+
}
|
|
29
|
+
export type ConfirmProblemCode = 'data_confirm_submit' | 'data_confirm_field' | 'data_confirm_text';
|
|
30
|
+
export declare function confirmEmailProblems(collection: string, declared: {
|
|
31
|
+
schema: Record<string, unknown>;
|
|
32
|
+
publicSubmit?: boolean;
|
|
33
|
+
confirmEmail?: ConfirmEmailDeclared;
|
|
34
|
+
}): {
|
|
35
|
+
code: ConfirmProblemCode;
|
|
36
|
+
collection: string;
|
|
37
|
+
field?: string;
|
|
38
|
+
message: string;
|
|
39
|
+
}[];
|
|
40
|
+
export declare function confirmEmailSpec(declared: ConfirmEmailDeclared): ConfirmEmailSpec;
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { FIELD_LIMITS, parseFieldType } from "./field-types.js";
|
|
3
|
+
/**
|
|
4
|
+
* The confirmation email of a public form (FO04). Mirrors Brydio's
|
|
5
|
+
* `apps/api/src/apps/manifest/confirm-email.ts`; the two change together.
|
|
6
|
+
*
|
|
7
|
+
* An app only declares it: which field of a `publicSubmit` collection holds
|
|
8
|
+
* the visitor's address, and a short plain text to say. Brydio decides the
|
|
9
|
+
* rest — when it is sent, to whom, how often, from whom — so an anonymous
|
|
10
|
+
* page can never be used to send anybody anything else.
|
|
11
|
+
*/
|
|
12
|
+
export const CONFIRM_LIMITS = {
|
|
13
|
+
subjectChars: 120,
|
|
14
|
+
messageChars: 600,
|
|
15
|
+
};
|
|
16
|
+
export const confirmEmailSchema = z
|
|
17
|
+
.object({
|
|
18
|
+
/** A `string` field of the same collection: the visitor's address. */
|
|
19
|
+
field: z.string().min(1).max(FIELD_LIMITS.nameChars),
|
|
20
|
+
subject: z.string().min(1).max(CONFIRM_LIMITS.subjectChars).optional(),
|
|
21
|
+
/** Plain text; a blank line starts a new paragraph. */
|
|
22
|
+
message: z.string().min(1).max(CONFIRM_LIMITS.messageChars).optional(),
|
|
23
|
+
/** One button back to the public page the visitor answered. */
|
|
24
|
+
link: z.boolean().optional(),
|
|
25
|
+
})
|
|
26
|
+
.strict();
|
|
27
|
+
/** A link, an address a client would turn into one, or markup. */
|
|
28
|
+
const NOT_PLAIN = /(?:[a-z][a-z0-9+.-]*:\/\/|www\.|[<>]|\]\(|mailto:)/i;
|
|
29
|
+
export function confirmEmailProblems(collection, declared) {
|
|
30
|
+
const confirm = declared.confirmEmail;
|
|
31
|
+
if (!confirm)
|
|
32
|
+
return [];
|
|
33
|
+
const problems = [];
|
|
34
|
+
if (!declared.publicSubmit) {
|
|
35
|
+
problems.push({
|
|
36
|
+
code: 'data_confirm_submit',
|
|
37
|
+
collection,
|
|
38
|
+
message: `${collection} sends a confirmation email but visitors can't submit to it: mark it publicSubmit.`,
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
const raw = declared.schema[confirm.field];
|
|
42
|
+
let kind = null;
|
|
43
|
+
try {
|
|
44
|
+
kind = raw === undefined ? null : parseFieldType(raw).kind;
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
kind = null;
|
|
48
|
+
}
|
|
49
|
+
if (kind !== 'string') {
|
|
50
|
+
problems.push({
|
|
51
|
+
code: 'data_confirm_field',
|
|
52
|
+
collection,
|
|
53
|
+
field: confirm.field,
|
|
54
|
+
message: `${collection}.confirmEmail.field must name one of its string fields; ${confirm.field} is not.`,
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
for (const [key, text] of [
|
|
58
|
+
['subject', confirm.subject],
|
|
59
|
+
['message', confirm.message],
|
|
60
|
+
]) {
|
|
61
|
+
if (text !== undefined && NOT_PLAIN.test(text)) {
|
|
62
|
+
problems.push({
|
|
63
|
+
code: 'data_confirm_text',
|
|
64
|
+
collection,
|
|
65
|
+
message: `${collection}.confirmEmail.${key} is plain text: no links, addresses or markup.`,
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return problems;
|
|
70
|
+
}
|
|
71
|
+
export function confirmEmailSpec(declared) {
|
|
72
|
+
return {
|
|
73
|
+
field: declared.field,
|
|
74
|
+
subject: declared.subject?.trim() || null,
|
|
75
|
+
message: declared.message?.trim() || null,
|
|
76
|
+
link: declared.link === true,
|
|
77
|
+
};
|
|
78
|
+
}
|
package/src/index.d.ts
CHANGED
|
@@ -9,6 +9,7 @@ export { BUNDLE_MANIFEST, BUNDLE_MAX_BYTES, BUNDLE_PATH_MAX_CHARS, bundleBytes,
|
|
|
9
9
|
export { COLOUR_TOKENS, coeditedFields, FIELD_LIMITS, RESERVED_FIELDS, describeType, isCoedited, isSearchable, isSortable, isStructured, parseFieldType, structuredFields, valueProblem, valueSchema, FieldTypeInvalid, type DocumentOf, type FieldKind, type FieldType, type FieldValue, type FieldsOf, } from './field-types.js';
|
|
10
10
|
export { appManifestSchema, collectionOf, collectionsOf, dataProblems, effectivePlacementKey, HOME_CARD_SIZES, labelOf, manifestExtensionsSchema, newAppManifestSchema, SIZED_PLACEMENT_KINDS, MAX_APP_SECRETS, MAX_SECRET_CHARS, PLACEMENT_KEY, SECRET_NAME, storedExtensionsSchema, type AppManifestWithData, type CollectionSpec, type DataProblem, type DataProblemCode, type DataProblemOptions, type HomeCardSize, type ManifestExtensions, type SecretSpec, } from './schema.js';
|
|
11
11
|
export { OPEN_LIMITS, OPEN_TYPES, choicesProblem, definitionOf, definitionTypeProblem, describeField, fieldTypeOf, hasChoices, isPlainOpenType, keyFromName, keyProblem, missingRequired, moveValue, openSchemaProblems, openSpec, openValueProblem, type Moved, type OpenField, type OpenFieldChange, type OpenFieldValue, type OpenProblem, type OpenProblemCode, type OpenSchemaFlag, type OpenType, } from './open-schema.js';
|
|
12
|
+
export { CONFIRM_LIMITS, confirmEmailProblems, confirmEmailSpec, type ConfirmEmailDeclared, type ConfirmEmailSpec, type ConfirmProblemCode, } from './confirm-email.js';
|
|
12
13
|
export { DOCUMENT_LIMITS } from './document-limits.js';
|
|
13
14
|
export { HOST_CAPABILITIES, KNOWN_HOST_GRANTS, isHostCapability, unknownHostGrant } from './grants.js';
|
|
14
15
|
export { diffSchemas, migrationProblems, migrationSchema, migrationStepSchema, migrationsSchema, publishedMigrationProblems, schemaOf, type Migration, type MigrationCode, type MigrationProblem, type MigrationStep, type SchemaChange, } from './migrations.js';
|
package/src/index.js
CHANGED
|
@@ -9,6 +9,7 @@ export { BUNDLE_MANIFEST, BUNDLE_MAX_BYTES, BUNDLE_PATH_MAX_CHARS, bundleBytes,
|
|
|
9
9
|
export { COLOUR_TOKENS, coeditedFields, FIELD_LIMITS, RESERVED_FIELDS, describeType, isCoedited, isSearchable, isSortable, isStructured, parseFieldType, structuredFields, valueProblem, valueSchema, FieldTypeInvalid, } from "./field-types.js";
|
|
10
10
|
export { appManifestSchema, collectionOf, collectionsOf, dataProblems, effectivePlacementKey, HOME_CARD_SIZES, labelOf, manifestExtensionsSchema, newAppManifestSchema, SIZED_PLACEMENT_KINDS, MAX_APP_SECRETS, MAX_SECRET_CHARS, PLACEMENT_KEY, SECRET_NAME, storedExtensionsSchema, } from "./schema.js";
|
|
11
11
|
export { OPEN_LIMITS, OPEN_TYPES, choicesProblem, definitionOf, definitionTypeProblem, describeField, fieldTypeOf, hasChoices, isPlainOpenType, keyFromName, keyProblem, missingRequired, moveValue, openSchemaProblems, openSpec, openValueProblem, } from "./open-schema.js";
|
|
12
|
+
export { CONFIRM_LIMITS, confirmEmailProblems, confirmEmailSpec, } from "./confirm-email.js";
|
|
12
13
|
export { DOCUMENT_LIMITS } from "./document-limits.js";
|
|
13
14
|
export { HOST_CAPABILITIES, KNOWN_HOST_GRANTS, isHostCapability, unknownHostGrant } from "./grants.js";
|
|
14
15
|
export { diffSchemas, migrationProblems, migrationSchema, migrationStepSchema, migrationsSchema, publishedMigrationProblems, schemaOf, } from "./migrations.js";
|
package/src/schema.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import { FieldTypeInvalid, type FieldType } from './field-types.js';
|
|
3
3
|
import { type OpenProblemCode } from './open-schema.js';
|
|
4
|
+
import { type ConfirmProblemCode } from './confirm-email.js';
|
|
4
5
|
export { COLOUR_TOKENS, FIELD_LIMITS, parseFieldType, RESERVED_FIELDS, structuredFields, type FieldKind, type FieldType, } from './field-types.js';
|
|
5
6
|
/**
|
|
6
7
|
* What an app adds to `.brydio/app.json` to be more than a bundle of servers
|
|
@@ -150,6 +151,12 @@ declare const extensionShape: {
|
|
|
150
151
|
}, z.core.$strict>>;
|
|
151
152
|
publicRead: z.ZodOptional<z.ZodBoolean>;
|
|
152
153
|
publicSubmit: z.ZodOptional<z.ZodBoolean>;
|
|
154
|
+
confirmEmail: z.ZodOptional<z.ZodObject<{
|
|
155
|
+
field: z.ZodString;
|
|
156
|
+
subject: z.ZodOptional<z.ZodString>;
|
|
157
|
+
message: z.ZodOptional<z.ZodString>;
|
|
158
|
+
link: z.ZodOptional<z.ZodBoolean>;
|
|
159
|
+
}, z.core.$strict>>;
|
|
153
160
|
}, z.core.$strip>>>;
|
|
154
161
|
tools: z.ZodOptional<z.ZodObject<{
|
|
155
162
|
generated: z.ZodOptional<z.ZodBoolean>;
|
|
@@ -228,7 +235,7 @@ export interface DataProblem {
|
|
|
228
235
|
field?: string;
|
|
229
236
|
message: string;
|
|
230
237
|
}
|
|
231
|
-
export type DataProblemCode = FieldTypeInvalid['code'] | OpenProblemCode | 'data_too_many_collections' | 'data_too_many_fields' | 'data_collection_name_format' | 'data_field_name_format' | 'data_field_reserved' | 'data_label_format' | 'data_label_taken' | 'data_project_field_twice' | 'data_search_unknown_field' | 'data_search_not_text' | 'grant_collection_missing' | 'grant_tool_missing' | 'custom_name_taken' | 'custom_collection_unknown' | 'custom_input_invalid' | 'placement_key_taken' | 'placement_screen_unknown' | 'placement_home_sizes' | 'placement_sizes_not_home' | 'placement_widget_sizes' | 'placement_tab_retired' | 'placement_public_shape' | 'placement_public_nothing' | 'placement_children_too_deep' | 'placement_create_tool_unknown' | 'placement_create_not_write' | 'secret_name_taken' | 'grant_secrets_missing';
|
|
238
|
+
export type DataProblemCode = FieldTypeInvalid['code'] | OpenProblemCode | ConfirmProblemCode | 'data_too_many_collections' | 'data_too_many_fields' | 'data_collection_name_format' | 'data_field_name_format' | 'data_field_reserved' | 'data_label_format' | 'data_label_taken' | 'data_project_field_twice' | 'data_search_unknown_field' | 'data_search_not_text' | 'grant_collection_missing' | 'grant_tool_missing' | 'custom_name_taken' | 'custom_collection_unknown' | 'custom_input_invalid' | 'placement_key_taken' | 'placement_screen_unknown' | 'placement_home_sizes' | 'placement_sizes_not_home' | 'placement_widget_sizes' | 'placement_tab_retired' | 'placement_public_shape' | 'placement_public_nothing' | 'placement_children_too_deep' | 'placement_create_tool_unknown' | 'placement_create_not_write' | 'secret_name_taken' | 'grant_secrets_missing';
|
|
232
239
|
type Additions = z.infer<z.ZodObject<typeof extensionShape>>;
|
|
233
240
|
/**
|
|
234
241
|
* Everything wrong with an app's additions that a type cannot say.
|
|
@@ -305,6 +312,12 @@ export declare const manifestExtensionsSchema: z.ZodObject<{
|
|
|
305
312
|
}, z.core.$strict>>;
|
|
306
313
|
publicRead: z.ZodOptional<z.ZodBoolean>;
|
|
307
314
|
publicSubmit: z.ZodOptional<z.ZodBoolean>;
|
|
315
|
+
confirmEmail: z.ZodOptional<z.ZodObject<{
|
|
316
|
+
field: z.ZodString;
|
|
317
|
+
subject: z.ZodOptional<z.ZodString>;
|
|
318
|
+
message: z.ZodOptional<z.ZodString>;
|
|
319
|
+
link: z.ZodOptional<z.ZodBoolean>;
|
|
320
|
+
}, z.core.$strict>>;
|
|
308
321
|
}, z.core.$strip>>>;
|
|
309
322
|
tools: z.ZodOptional<z.ZodObject<{
|
|
310
323
|
generated: z.ZodOptional<z.ZodBoolean>;
|
|
@@ -430,6 +443,12 @@ export declare const storedExtensionsSchema: z.ZodObject<{
|
|
|
430
443
|
}, z.core.$strict>>;
|
|
431
444
|
publicRead: z.ZodOptional<z.ZodBoolean>;
|
|
432
445
|
publicSubmit: z.ZodOptional<z.ZodBoolean>;
|
|
446
|
+
confirmEmail: z.ZodOptional<z.ZodObject<{
|
|
447
|
+
field: z.ZodString;
|
|
448
|
+
subject: z.ZodOptional<z.ZodString>;
|
|
449
|
+
message: z.ZodOptional<z.ZodString>;
|
|
450
|
+
link: z.ZodOptional<z.ZodBoolean>;
|
|
451
|
+
}, z.core.$strict>>;
|
|
433
452
|
}, z.core.$strip>>>;
|
|
434
453
|
tools: z.ZodOptional<z.ZodObject<{
|
|
435
454
|
generated: z.ZodOptional<z.ZodBoolean>;
|
|
@@ -590,6 +609,12 @@ export declare const appManifestSchema: z.ZodObject<{
|
|
|
590
609
|
}, z.core.$strict>>;
|
|
591
610
|
publicRead: z.ZodOptional<z.ZodBoolean>;
|
|
592
611
|
publicSubmit: z.ZodOptional<z.ZodBoolean>;
|
|
612
|
+
confirmEmail: z.ZodOptional<z.ZodObject<{
|
|
613
|
+
field: z.ZodString;
|
|
614
|
+
subject: z.ZodOptional<z.ZodString>;
|
|
615
|
+
message: z.ZodOptional<z.ZodString>;
|
|
616
|
+
link: z.ZodOptional<z.ZodBoolean>;
|
|
617
|
+
}, z.core.$strict>>;
|
|
593
618
|
}, z.core.$strip>>>;
|
|
594
619
|
tools: z.ZodOptional<z.ZodObject<{
|
|
595
620
|
generated: z.ZodOptional<z.ZodBoolean>;
|
|
@@ -754,6 +779,12 @@ export declare const newAppManifestSchema: z.ZodObject<{
|
|
|
754
779
|
}, z.core.$strict>>;
|
|
755
780
|
publicRead: z.ZodOptional<z.ZodBoolean>;
|
|
756
781
|
publicSubmit: z.ZodOptional<z.ZodBoolean>;
|
|
782
|
+
confirmEmail: z.ZodOptional<z.ZodObject<{
|
|
783
|
+
field: z.ZodString;
|
|
784
|
+
subject: z.ZodOptional<z.ZodString>;
|
|
785
|
+
message: z.ZodOptional<z.ZodString>;
|
|
786
|
+
link: z.ZodOptional<z.ZodBoolean>;
|
|
787
|
+
}, z.core.$strict>>;
|
|
757
788
|
}, z.core.$strip>>>;
|
|
758
789
|
tools: z.ZodOptional<z.ZodObject<{
|
|
759
790
|
generated: z.ZodOptional<z.ZodBoolean>;
|
package/src/schema.js
CHANGED
|
@@ -3,6 +3,7 @@ import { MANIFEST_LIMITS, SEMVER_FORMAT, baseManifestSchema as manifestSchema }
|
|
|
3
3
|
import { COLLECTION_NAME, FIELD_LIMITS, FIELD_NAME, FieldTypeInvalid, isSearchable, isSortable, isStructured, parseFieldType, RESERVED_FIELDS, } from "./field-types.js";
|
|
4
4
|
import { migrationsSchema } from "./migrations.js";
|
|
5
5
|
import { openSchemaProblems } from "./open-schema.js";
|
|
6
|
+
import { confirmEmailProblems, confirmEmailSchema } from "./confirm-email.js";
|
|
6
7
|
export { COLOUR_TOKENS, FIELD_LIMITS, parseFieldType, RESERVED_FIELDS, structuredFields, } from "./field-types.js";
|
|
7
8
|
/**
|
|
8
9
|
* What an app adds to `.brydio/app.json` to be more than a bundle of servers
|
|
@@ -130,6 +131,13 @@ const collectionSchema = z.object({
|
|
|
130
131
|
* `publicRead` is set too (P3).
|
|
131
132
|
*/
|
|
132
133
|
publicSubmit: z.boolean().optional(),
|
|
134
|
+
/**
|
|
135
|
+
* Brydio emails the visitor once after a public submission here (FO04):
|
|
136
|
+
* `field` names a `string` field holding their address; `subject` and
|
|
137
|
+
* `message` are short plain text; `link` adds a button back to the page.
|
|
138
|
+
* Needs `publicSubmit`. Brydio decides when, how often and from whom.
|
|
139
|
+
*/
|
|
140
|
+
confirmEmail: confirmEmailSchema.optional(),
|
|
133
141
|
});
|
|
134
142
|
const screenSchema = z.object({
|
|
135
143
|
/**
|
|
@@ -359,6 +367,10 @@ export function dataProblems(additions, options = {}) {
|
|
|
359
367
|
}
|
|
360
368
|
companions.set(declared.openSchema.fields, collection);
|
|
361
369
|
}
|
|
370
|
+
// A confirmation email names a string field of a collection visitors submit to (FO04).
|
|
371
|
+
for (const [collection, declared] of collections) {
|
|
372
|
+
problems.push(...confirmEmailProblems(collection, declared));
|
|
373
|
+
}
|
|
362
374
|
// What the app keeps must be what it asks to keep (A3-F06-S01): a
|
|
363
375
|
// collection the grants leave out would be data a workspace never agreed to
|
|
364
376
|
// hold, found only when the first write is refused.
|