@brydio/manifest 0.1.0-alpha.37 → 0.1.0-alpha.38
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/index.d.ts +1 -0
- package/src/index.js +1 -0
- package/src/readers.d.ts +31 -0
- package/src/readers.js +59 -0
- package/src/schema.d.ts +9 -1
- package/src/schema.js +8 -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.38",
|
|
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": "4b6c80dcb849195171699f16022aab1f55d6e3b6",
|
|
14
14
|
"exports": {
|
|
15
15
|
".": {
|
|
16
16
|
"types": "./src/index.d.ts",
|
package/src/index.d.ts
CHANGED
|
@@ -11,6 +11,7 @@ export { appManifestSchema, appToolPrefix, collectionOf, crossAppTool, collectio
|
|
|
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
12
|
export { CONFIRM_LIMITS, confirmEmailProblems, confirmEmailSpec, type ConfirmEmailDeclared, type ConfirmEmailSpec, type ConfirmProblemCode, } from './confirm-email.js';
|
|
13
13
|
export { ANONYMOUS_LIMITS, ANONYMOUS_WRITER, anonymousProblems, anonymousSpec, type AnonymousDeclared, type AnonymousProblemCode, type AnonymousSpec, } from './anonymous.js';
|
|
14
|
+
export { READERS_LIMIT, readersOf, readersProblems, type ReadersProblemCode } from './readers.js';
|
|
14
15
|
export { DOCUMENT_LIMITS } from './document-limits.js';
|
|
15
16
|
export { HOST_CAPABILITIES, KNOWN_HOST_GRANTS, isHostCapability, unknownHostGrant } from './grants.js';
|
|
16
17
|
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
|
@@ -11,6 +11,7 @@ export { appManifestSchema, appToolPrefix, collectionOf, crossAppTool, collectio
|
|
|
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
12
|
export { CONFIRM_LIMITS, confirmEmailProblems, confirmEmailSpec, } from "./confirm-email.js";
|
|
13
13
|
export { ANONYMOUS_LIMITS, ANONYMOUS_WRITER, anonymousProblems, anonymousSpec, } from "./anonymous.js";
|
|
14
|
+
export { READERS_LIMIT, readersOf, readersProblems } from "./readers.js";
|
|
14
15
|
export { DOCUMENT_LIMITS } from "./document-limits.js";
|
|
15
16
|
export { HOST_CAPABILITIES, KNOWN_HOST_GRANTS, isHostCapability, unknownHostGrant } from "./grants.js";
|
|
16
17
|
export { diffSchemas, migrationProblems, migrationSchema, migrationStepSchema, migrationsSchema, publishedMigrationProblems, schemaOf, } from "./migrations.js";
|
package/src/readers.d.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
/**
|
|
3
|
+
* Record readers (P16): a record only the people it
|
|
4
|
+
* names may read.
|
|
5
|
+
*
|
|
6
|
+
* An app names one of the collection's own `string[]` fields, which holds
|
|
7
|
+
* user ids. When a record's list is empty, everyone who can read the
|
|
8
|
+
* instance reads it, as before; when it names anyone, nobody else finds it —
|
|
9
|
+
* not on a screen, not through a tool, not through the assistant, not on the
|
|
10
|
+
* co-edit socket. There is no admin past it: an app that wants its admins
|
|
11
|
+
* in puts them in the list. Brydio keeps the list in plain beside the record
|
|
12
|
+
* (`app_document.readers`), so every read can be narrowed before anything
|
|
13
|
+
* is decrypted. Mirrors Brydio's `apps/api/src/apps/manifest/readers.ts`;
|
|
14
|
+
* the two change together.
|
|
15
|
+
*/
|
|
16
|
+
export declare const readersSchema: z.ZodString;
|
|
17
|
+
export type ReadersProblemCode = 'data_readers_field' | 'data_readers_anonymous';
|
|
18
|
+
/** The most people one record's list may name. */
|
|
19
|
+
export declare const READERS_LIMIT = 1000;
|
|
20
|
+
export declare function readersProblems(collection: string, declared: {
|
|
21
|
+
schema: Record<string, unknown>;
|
|
22
|
+
readers?: string;
|
|
23
|
+
anonymous?: unknown;
|
|
24
|
+
}): {
|
|
25
|
+
code: ReadersProblemCode;
|
|
26
|
+
collection: string;
|
|
27
|
+
field?: string;
|
|
28
|
+
message: string;
|
|
29
|
+
}[];
|
|
30
|
+
/** The plain list a record is kept with: its readers, or null for everyone who can read the instance. */
|
|
31
|
+
export declare function readersOf(field: string | undefined, body: Record<string, unknown>): string[] | null;
|
package/src/readers.js
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { FIELD_LIMITS, parseFieldType } from "./field-types.js";
|
|
3
|
+
/**
|
|
4
|
+
* Record readers (P16): a record only the people it
|
|
5
|
+
* names may read.
|
|
6
|
+
*
|
|
7
|
+
* An app names one of the collection's own `string[]` fields, which holds
|
|
8
|
+
* user ids. When a record's list is empty, everyone who can read the
|
|
9
|
+
* instance reads it, as before; when it names anyone, nobody else finds it —
|
|
10
|
+
* not on a screen, not through a tool, not through the assistant, not on the
|
|
11
|
+
* co-edit socket. There is no admin past it: an app that wants its admins
|
|
12
|
+
* in puts them in the list. Brydio keeps the list in plain beside the record
|
|
13
|
+
* (`app_document.readers`), so every read can be narrowed before anything
|
|
14
|
+
* is decrypted. Mirrors Brydio's `apps/api/src/apps/manifest/readers.ts`;
|
|
15
|
+
* the two change together.
|
|
16
|
+
*/
|
|
17
|
+
export const readersSchema = z.string().min(1).max(FIELD_LIMITS.nameChars);
|
|
18
|
+
/** The most people one record's list may name. */
|
|
19
|
+
export const READERS_LIMIT = 1_000;
|
|
20
|
+
export function readersProblems(collection, declared) {
|
|
21
|
+
if (!declared.readers)
|
|
22
|
+
return [];
|
|
23
|
+
const problems = [];
|
|
24
|
+
const raw = declared.schema[declared.readers];
|
|
25
|
+
let listed = false;
|
|
26
|
+
try {
|
|
27
|
+
listed = raw !== undefined && parseFieldType(raw).kind === 'string[]';
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
// Refused as a field type already.
|
|
31
|
+
}
|
|
32
|
+
if (!listed) {
|
|
33
|
+
problems.push({
|
|
34
|
+
code: 'data_readers_field',
|
|
35
|
+
collection,
|
|
36
|
+
field: declared.readers,
|
|
37
|
+
message: `"${collection}" names "${declared.readers}" as its readers, which must be one of its own string[] fields holding user ids.`,
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
// An anonymous answer names nobody; a list of who may read it would.
|
|
41
|
+
if (declared.anonymous) {
|
|
42
|
+
problems.push({
|
|
43
|
+
code: 'data_readers_anonymous',
|
|
44
|
+
collection,
|
|
45
|
+
message: `"${collection}" can't be anonymous and name its readers.`,
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
return problems;
|
|
49
|
+
}
|
|
50
|
+
/** The plain list a record is kept with: its readers, or null for everyone who can read the instance. */
|
|
51
|
+
export function readersOf(field, body) {
|
|
52
|
+
if (!field)
|
|
53
|
+
return null;
|
|
54
|
+
const value = body[field];
|
|
55
|
+
if (!Array.isArray(value))
|
|
56
|
+
return null;
|
|
57
|
+
const ids = [...new Set(value.filter((one) => typeof one === 'string' && one.length > 0))];
|
|
58
|
+
return ids.length ? ids.sort() : null;
|
|
59
|
+
}
|
package/src/schema.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { FieldTypeInvalid, type FieldType } from './field-types.js';
|
|
|
3
3
|
import { type OpenProblemCode } from './open-schema.js';
|
|
4
4
|
import { type ConfirmProblemCode } from './confirm-email.js';
|
|
5
5
|
import { type AnonymousProblemCode, type AnonymousSpec } from './anonymous.js';
|
|
6
|
+
import { type ReadersProblemCode } from './readers.js';
|
|
6
7
|
export { COLOUR_TOKENS, FIELD_LIMITS, parseFieldType, RESERVED_FIELDS, structuredFields, type FieldKind, type FieldType, } from './field-types.js';
|
|
7
8
|
/**
|
|
8
9
|
* What an app adds to `.brydio/app.json` to be more than a bundle of servers
|
|
@@ -163,6 +164,7 @@ declare const extensionShape: {
|
|
|
163
164
|
group: z.ZodString;
|
|
164
165
|
minimum: z.ZodOptional<z.ZodNumber>;
|
|
165
166
|
}, z.core.$strict>>;
|
|
167
|
+
readers: z.ZodOptional<z.ZodString>;
|
|
166
168
|
}, z.core.$strip>>>;
|
|
167
169
|
tools: z.ZodOptional<z.ZodObject<{
|
|
168
170
|
generated: z.ZodOptional<z.ZodBoolean>;
|
|
@@ -241,7 +243,7 @@ export interface DataProblem {
|
|
|
241
243
|
field?: string;
|
|
242
244
|
message: string;
|
|
243
245
|
}
|
|
244
|
-
export type DataProblemCode = FieldTypeInvalid['code'] | OpenProblemCode | ConfirmProblemCode | AnonymousProblemCode | '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_chat_card_shape' | 'grant_tool_cross_app_self' | 'placement_children_too_deep' | 'placement_create_tool_unknown' | 'placement_create_not_write' | 'secret_name_taken' | 'grant_secrets_missing';
|
|
246
|
+
export type DataProblemCode = FieldTypeInvalid['code'] | OpenProblemCode | ConfirmProblemCode | AnonymousProblemCode | ReadersProblemCode | '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_chat_card_shape' | 'grant_tool_cross_app_self' | 'placement_children_too_deep' | 'placement_create_tool_unknown' | 'placement_create_not_write' | 'secret_name_taken' | 'grant_secrets_missing';
|
|
245
247
|
type Additions = z.infer<z.ZodObject<typeof extensionShape>>;
|
|
246
248
|
/**
|
|
247
249
|
* Everything wrong with an app's additions that a type cannot say.
|
|
@@ -340,6 +342,7 @@ export declare const manifestExtensionsSchema: z.ZodObject<{
|
|
|
340
342
|
group: z.ZodString;
|
|
341
343
|
minimum: z.ZodOptional<z.ZodNumber>;
|
|
342
344
|
}, z.core.$strict>>;
|
|
345
|
+
readers: z.ZodOptional<z.ZodString>;
|
|
343
346
|
}, z.core.$strip>>>;
|
|
344
347
|
tools: z.ZodOptional<z.ZodObject<{
|
|
345
348
|
generated: z.ZodOptional<z.ZodBoolean>;
|
|
@@ -476,6 +479,7 @@ export declare const storedExtensionsSchema: z.ZodObject<{
|
|
|
476
479
|
group: z.ZodString;
|
|
477
480
|
minimum: z.ZodOptional<z.ZodNumber>;
|
|
478
481
|
}, z.core.$strict>>;
|
|
482
|
+
readers: z.ZodOptional<z.ZodString>;
|
|
479
483
|
}, z.core.$strip>>>;
|
|
480
484
|
tools: z.ZodOptional<z.ZodObject<{
|
|
481
485
|
generated: z.ZodOptional<z.ZodBoolean>;
|
|
@@ -647,6 +651,7 @@ export declare const appManifestSchema: z.ZodObject<{
|
|
|
647
651
|
group: z.ZodString;
|
|
648
652
|
minimum: z.ZodOptional<z.ZodNumber>;
|
|
649
653
|
}, z.core.$strict>>;
|
|
654
|
+
readers: z.ZodOptional<z.ZodString>;
|
|
650
655
|
}, z.core.$strip>>>;
|
|
651
656
|
tools: z.ZodOptional<z.ZodObject<{
|
|
652
657
|
generated: z.ZodOptional<z.ZodBoolean>;
|
|
@@ -822,6 +827,7 @@ export declare const newAppManifestSchema: z.ZodObject<{
|
|
|
822
827
|
group: z.ZodString;
|
|
823
828
|
minimum: z.ZodOptional<z.ZodNumber>;
|
|
824
829
|
}, z.core.$strict>>;
|
|
830
|
+
readers: z.ZodOptional<z.ZodString>;
|
|
825
831
|
}, z.core.$strip>>>;
|
|
826
832
|
tools: z.ZodOptional<z.ZodObject<{
|
|
827
833
|
generated: z.ZodOptional<z.ZodBoolean>;
|
|
@@ -914,6 +920,8 @@ export interface CollectionSpec {
|
|
|
914
920
|
definesFieldsOf?: string;
|
|
915
921
|
/** Answers kept with no writer, read only a whole group of at least `minimum` at a time (P13). */
|
|
916
922
|
anonymous?: AnonymousSpec;
|
|
923
|
+
/** The string[] field naming who alone may read a record, when it names anyone (P16). */
|
|
924
|
+
readers?: string;
|
|
917
925
|
}
|
|
918
926
|
/**
|
|
919
927
|
* The singular a collection's tools are named with: the manifest's, else the
|
package/src/schema.js
CHANGED
|
@@ -5,6 +5,7 @@ import { migrationsSchema } from "./migrations.js";
|
|
|
5
5
|
import { openSchemaProblems } from "./open-schema.js";
|
|
6
6
|
import { confirmEmailProblems, confirmEmailSchema } from "./confirm-email.js";
|
|
7
7
|
import { anonymousProblems, anonymousSchema, anonymousSpec } from "./anonymous.js";
|
|
8
|
+
import { readersProblems, readersSchema } from "./readers.js";
|
|
8
9
|
export { COLOUR_TOKENS, FIELD_LIMITS, parseFieldType, RESERVED_FIELDS, structuredFields, } from "./field-types.js";
|
|
9
10
|
/**
|
|
10
11
|
* What an app adds to `.brydio/app.json` to be more than a bundle of servers
|
|
@@ -152,6 +153,8 @@ const collectionSchema = z.object({
|
|
|
152
153
|
* batch, a watch and a create naming its own writer.
|
|
153
154
|
*/
|
|
154
155
|
anonymous: anonymousSchema.optional(),
|
|
156
|
+
/** One of its string[] fields: the user ids who alone may read a record, when it names any (P16). */
|
|
157
|
+
readers: readersSchema.optional(),
|
|
155
158
|
});
|
|
156
159
|
const screenSchema = z.object({
|
|
157
160
|
/**
|
|
@@ -389,6 +392,10 @@ export function dataProblems(additions, options = {}) {
|
|
|
389
392
|
for (const [collection, declared] of collections) {
|
|
390
393
|
problems.push(...anonymousProblems(collection, declared));
|
|
391
394
|
}
|
|
395
|
+
// A collection whose records name their readers names a list of user ids to keep them in (P16).
|
|
396
|
+
for (const [collection, declared] of collections) {
|
|
397
|
+
problems.push(...readersProblems(collection, declared));
|
|
398
|
+
}
|
|
392
399
|
// What the app keeps must be what it asks to keep (A3-F06-S01): a
|
|
393
400
|
// collection the grants leave out would be data a workspace never agreed to
|
|
394
401
|
// hold, found only when the first write is refused.
|
|
@@ -738,6 +745,7 @@ export function collectionsOf(manifest) {
|
|
|
738
745
|
: {}),
|
|
739
746
|
...(owner ? { definesFieldsOf: owner.name } : {}),
|
|
740
747
|
...(declared.anonymous ? { anonymous: anonymousSpec(declared.anonymous) } : {}),
|
|
748
|
+
...(declared.readers ? { readers: declared.readers } : {}),
|
|
741
749
|
};
|
|
742
750
|
});
|
|
743
751
|
}
|