@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@brydio/manifest",
3
- "version": "0.1.0-alpha.34",
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": "4da27394c9c0243956b58a6dfdc983b691e67ef6",
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.