@brydio/manifest 0.1.0-alpha.38 → 0.1.0-alpha.39
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/field-types.d.ts +6 -0
- package/src/field-types.js +3 -3
- package/src/index.d.ts +1 -1
- package/src/index.js +1 -1
- package/src/readers.d.ts +23 -0
- package/src/readers.js +37 -0
- package/src/schema.d.ts +14 -7
- package/src/schema.js +24 -13
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.39",
|
|
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": "4b20d567d9361683e104ec609d231111bd93bcbf",
|
|
14
14
|
"exports": {
|
|
15
15
|
".": {
|
|
16
16
|
"types": "./src/index.d.ts",
|
package/src/field-types.d.ts
CHANGED
|
@@ -40,6 +40,12 @@ export interface FieldType {
|
|
|
40
40
|
* row's table. Set by the host only, never parsed from a manifest.
|
|
41
41
|
*/
|
|
42
42
|
plain?: boolean;
|
|
43
|
+
/**
|
|
44
|
+
* The most entries a `string[]` holds, when not `FIELD_LIMITS.listEntries`:
|
|
45
|
+
* a collection's readers or editors field takes `READERS_LIMIT` (P16,
|
|
46
|
+
* DW06). Set by the host only, never parsed from a manifest.
|
|
47
|
+
*/
|
|
48
|
+
maxEntries?: number;
|
|
43
49
|
}
|
|
44
50
|
/**
|
|
45
51
|
* The bounds a schema and a record are held to (A3-F01-S03).
|
package/src/field-types.js
CHANGED
|
@@ -259,7 +259,7 @@ export function valueSchema(type) {
|
|
|
259
259
|
case 'boolean':
|
|
260
260
|
return z.boolean();
|
|
261
261
|
case 'string[]':
|
|
262
|
-
return z.array(z.string().max(FIELD_LIMITS.stringChars)).max(FIELD_LIMITS.listEntries);
|
|
262
|
+
return z.array(z.string().max(FIELD_LIMITS.stringChars)).max(type.maxEntries ?? FIELD_LIMITS.listEntries);
|
|
263
263
|
case 'canvas':
|
|
264
264
|
return z.object({ elements: z.number().int().min(0), text: z.string().max(CANVAS_SUMMARY_CHARS) }).strict();
|
|
265
265
|
}
|
|
@@ -294,8 +294,8 @@ export function valueProblem(field, type, value) {
|
|
|
294
294
|
case 'boolean':
|
|
295
295
|
return `${field} must be true or false.`;
|
|
296
296
|
case 'string[]':
|
|
297
|
-
return Array.isArray(value) && value.length > FIELD_LIMITS.listEntries
|
|
298
|
-
? `${field} may hold at most ${FIELD_LIMITS.listEntries} entries.`
|
|
297
|
+
return Array.isArray(value) && value.length > (type.maxEntries ?? FIELD_LIMITS.listEntries)
|
|
298
|
+
? `${field} may hold at most ${type.maxEntries ?? FIELD_LIMITS.listEntries} entries.`
|
|
299
299
|
: `${field} must be a list of short texts, each at most ${FIELD_LIMITS.stringChars.toLocaleString('en-GB')} characters.`;
|
|
300
300
|
case 'canvas':
|
|
301
301
|
return `${field} is a drawing: it is changed on its board, never written.`;
|
package/src/index.d.ts
CHANGED
|
@@ -11,7 +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
|
+
export { editorsOf, editorsProblems, READERS_LIMIT, readersOf, readersProblems, type EditorsProblemCode, type ReadersProblemCode } from './readers.js';
|
|
15
15
|
export { DOCUMENT_LIMITS } from './document-limits.js';
|
|
16
16
|
export { HOST_CAPABILITIES, KNOWN_HOST_GRANTS, isHostCapability, unknownHostGrant } from './grants.js';
|
|
17
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,7 +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
|
+
export { editorsOf, editorsProblems, READERS_LIMIT, readersOf, readersProblems } from "./readers.js";
|
|
15
15
|
export { DOCUMENT_LIMITS } from "./document-limits.js";
|
|
16
16
|
export { HOST_CAPABILITIES, KNOWN_HOST_GRANTS, isHostCapability, unknownHostGrant } from "./grants.js";
|
|
17
17
|
export { diffSchemas, migrationProblems, migrationSchema, migrationStepSchema, migrationsSchema, publishedMigrationProblems, schemaOf, } from "./migrations.js";
|
package/src/readers.d.ts
CHANGED
|
@@ -29,3 +29,26 @@ export declare function readersProblems(collection: string, declared: {
|
|
|
29
29
|
}[];
|
|
30
30
|
/** The plain list a record is kept with: its readers, or null for everyone who can read the instance. */
|
|
31
31
|
export declare function readersOf(field: string | undefined, body: Record<string, unknown>): string[] | null;
|
|
32
|
+
/**
|
|
33
|
+
* Record editors (DW06): a record only the people it names may change. The
|
|
34
|
+
* same shape as readers: one of the collection's own `string[]` fields of
|
|
35
|
+
* user ids, kept in plain beside the record (`app_document.editors`). Empty,
|
|
36
|
+
* and anyone who may write the instance may change it; naming anyone, and
|
|
37
|
+
* only they may — through a tool, a handler, the assistant or the co-edit
|
|
38
|
+
* socket, where anyone else's connection is read-only. Everyone who may read
|
|
39
|
+
* the record still reads it.
|
|
40
|
+
*/
|
|
41
|
+
export declare const editorsSchema: z.ZodString;
|
|
42
|
+
export type EditorsProblemCode = 'data_editors_field' | 'data_editors_anonymous';
|
|
43
|
+
export declare function editorsProblems(collection: string, declared: {
|
|
44
|
+
schema: Record<string, unknown>;
|
|
45
|
+
editors?: string;
|
|
46
|
+
anonymous?: unknown;
|
|
47
|
+
}): {
|
|
48
|
+
code: EditorsProblemCode;
|
|
49
|
+
collection: string;
|
|
50
|
+
field?: string;
|
|
51
|
+
message: string;
|
|
52
|
+
}[];
|
|
53
|
+
/** The plain list a record is kept with: its editors, or null for anyone who may write the instance. */
|
|
54
|
+
export declare const editorsOf: typeof readersOf;
|
package/src/readers.js
CHANGED
|
@@ -57,3 +57,40 @@ export function readersOf(field, body) {
|
|
|
57
57
|
const ids = [...new Set(value.filter((one) => typeof one === 'string' && one.length > 0))];
|
|
58
58
|
return ids.length ? ids.sort() : null;
|
|
59
59
|
}
|
|
60
|
+
/**
|
|
61
|
+
* Record editors (DW06): a record only the people it names may change. The
|
|
62
|
+
* same shape as readers: one of the collection's own `string[]` fields of
|
|
63
|
+
* user ids, kept in plain beside the record (`app_document.editors`). Empty,
|
|
64
|
+
* and anyone who may write the instance may change it; naming anyone, and
|
|
65
|
+
* only they may — through a tool, a handler, the assistant or the co-edit
|
|
66
|
+
* socket, where anyone else's connection is read-only. Everyone who may read
|
|
67
|
+
* the record still reads it.
|
|
68
|
+
*/
|
|
69
|
+
export const editorsSchema = readersSchema;
|
|
70
|
+
export function editorsProblems(collection, declared) {
|
|
71
|
+
if (!declared.editors)
|
|
72
|
+
return [];
|
|
73
|
+
const problems = [];
|
|
74
|
+
const raw = declared.schema[declared.editors];
|
|
75
|
+
let listed = false;
|
|
76
|
+
try {
|
|
77
|
+
listed = raw !== undefined && parseFieldType(raw).kind === 'string[]';
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
// Refused as a field type already.
|
|
81
|
+
}
|
|
82
|
+
if (!listed) {
|
|
83
|
+
problems.push({
|
|
84
|
+
code: 'data_editors_field',
|
|
85
|
+
collection,
|
|
86
|
+
field: declared.editors,
|
|
87
|
+
message: `"${collection}" names "${declared.editors}" as its editors, which must be one of its own string[] fields holding user ids.`,
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
if (declared.anonymous) {
|
|
91
|
+
problems.push({ code: 'data_editors_anonymous', collection, message: `"${collection}" can't be anonymous and name its editors.` });
|
|
92
|
+
}
|
|
93
|
+
return problems;
|
|
94
|
+
}
|
|
95
|
+
/** The plain list a record is kept with: its editors, or null for anyone who may write the instance. */
|
|
96
|
+
export const editorsOf = readersOf;
|
package/src/schema.d.ts
CHANGED
|
@@ -3,7 +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
|
+
import { type EditorsProblemCode, type ReadersProblemCode } from './readers.js';
|
|
7
7
|
export { COLOUR_TOKENS, FIELD_LIMITS, parseFieldType, RESERVED_FIELDS, structuredFields, type FieldKind, type FieldType, } from './field-types.js';
|
|
8
8
|
/**
|
|
9
9
|
* What an app adds to `.brydio/app.json` to be more than a bundle of servers
|
|
@@ -165,9 +165,10 @@ declare const extensionShape: {
|
|
|
165
165
|
minimum: z.ZodOptional<z.ZodNumber>;
|
|
166
166
|
}, z.core.$strict>>;
|
|
167
167
|
readers: z.ZodOptional<z.ZodString>;
|
|
168
|
+
editors: z.ZodOptional<z.ZodString>;
|
|
168
169
|
}, z.core.$strip>>>;
|
|
169
170
|
tools: z.ZodOptional<z.ZodObject<{
|
|
170
|
-
generated: z.ZodOptional<z.ZodBoolean
|
|
171
|
+
generated: z.ZodOptional<z.ZodUnion<readonly [z.ZodBoolean, z.ZodLiteral<"read">]>>;
|
|
171
172
|
custom: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
172
173
|
name: z.ZodString;
|
|
173
174
|
description: z.ZodString;
|
|
@@ -243,7 +244,7 @@ export interface DataProblem {
|
|
|
243
244
|
field?: string;
|
|
244
245
|
message: string;
|
|
245
246
|
}
|
|
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';
|
|
247
|
+
export type DataProblemCode = FieldTypeInvalid['code'] | OpenProblemCode | ConfirmProblemCode | AnonymousProblemCode | ReadersProblemCode | EditorsProblemCode | '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';
|
|
247
248
|
type Additions = z.infer<z.ZodObject<typeof extensionShape>>;
|
|
248
249
|
/**
|
|
249
250
|
* Everything wrong with an app's additions that a type cannot say.
|
|
@@ -343,9 +344,10 @@ export declare const manifestExtensionsSchema: z.ZodObject<{
|
|
|
343
344
|
minimum: z.ZodOptional<z.ZodNumber>;
|
|
344
345
|
}, z.core.$strict>>;
|
|
345
346
|
readers: z.ZodOptional<z.ZodString>;
|
|
347
|
+
editors: z.ZodOptional<z.ZodString>;
|
|
346
348
|
}, z.core.$strip>>>;
|
|
347
349
|
tools: z.ZodOptional<z.ZodObject<{
|
|
348
|
-
generated: z.ZodOptional<z.ZodBoolean
|
|
350
|
+
generated: z.ZodOptional<z.ZodUnion<readonly [z.ZodBoolean, z.ZodLiteral<"read">]>>;
|
|
349
351
|
custom: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
350
352
|
name: z.ZodString;
|
|
351
353
|
description: z.ZodString;
|
|
@@ -480,9 +482,10 @@ export declare const storedExtensionsSchema: z.ZodObject<{
|
|
|
480
482
|
minimum: z.ZodOptional<z.ZodNumber>;
|
|
481
483
|
}, z.core.$strict>>;
|
|
482
484
|
readers: z.ZodOptional<z.ZodString>;
|
|
485
|
+
editors: z.ZodOptional<z.ZodString>;
|
|
483
486
|
}, z.core.$strip>>>;
|
|
484
487
|
tools: z.ZodOptional<z.ZodObject<{
|
|
485
|
-
generated: z.ZodOptional<z.ZodBoolean
|
|
488
|
+
generated: z.ZodOptional<z.ZodUnion<readonly [z.ZodBoolean, z.ZodLiteral<"read">]>>;
|
|
486
489
|
custom: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
487
490
|
name: z.ZodString;
|
|
488
491
|
description: z.ZodString;
|
|
@@ -652,9 +655,10 @@ export declare const appManifestSchema: z.ZodObject<{
|
|
|
652
655
|
minimum: z.ZodOptional<z.ZodNumber>;
|
|
653
656
|
}, z.core.$strict>>;
|
|
654
657
|
readers: z.ZodOptional<z.ZodString>;
|
|
658
|
+
editors: z.ZodOptional<z.ZodString>;
|
|
655
659
|
}, z.core.$strip>>>;
|
|
656
660
|
tools: z.ZodOptional<z.ZodObject<{
|
|
657
|
-
generated: z.ZodOptional<z.ZodBoolean
|
|
661
|
+
generated: z.ZodOptional<z.ZodUnion<readonly [z.ZodBoolean, z.ZodLiteral<"read">]>>;
|
|
658
662
|
custom: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
659
663
|
name: z.ZodString;
|
|
660
664
|
description: z.ZodString;
|
|
@@ -828,9 +832,10 @@ export declare const newAppManifestSchema: z.ZodObject<{
|
|
|
828
832
|
minimum: z.ZodOptional<z.ZodNumber>;
|
|
829
833
|
}, z.core.$strict>>;
|
|
830
834
|
readers: z.ZodOptional<z.ZodString>;
|
|
835
|
+
editors: z.ZodOptional<z.ZodString>;
|
|
831
836
|
}, z.core.$strip>>>;
|
|
832
837
|
tools: z.ZodOptional<z.ZodObject<{
|
|
833
|
-
generated: z.ZodOptional<z.ZodBoolean
|
|
838
|
+
generated: z.ZodOptional<z.ZodUnion<readonly [z.ZodBoolean, z.ZodLiteral<"read">]>>;
|
|
834
839
|
custom: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
835
840
|
name: z.ZodString;
|
|
836
841
|
description: z.ZodString;
|
|
@@ -922,6 +927,8 @@ export interface CollectionSpec {
|
|
|
922
927
|
anonymous?: AnonymousSpec;
|
|
923
928
|
/** The string[] field naming who alone may read a record, when it names anyone (P16). */
|
|
924
929
|
readers?: string;
|
|
930
|
+
/** The string[] field naming who alone may change a record, when it names anyone (DW06). */
|
|
931
|
+
editors?: string;
|
|
925
932
|
}
|
|
926
933
|
/**
|
|
927
934
|
* The singular a collection's tools are named with: the manifest's, else the
|
package/src/schema.js
CHANGED
|
@@ -5,7 +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
|
+
import { editorsProblems, editorsSchema, READERS_LIMIT, readersProblems, readersSchema } from "./readers.js";
|
|
9
9
|
export { COLOUR_TOKENS, FIELD_LIMITS, parseFieldType, RESERVED_FIELDS, structuredFields, } from "./field-types.js";
|
|
10
10
|
/**
|
|
11
11
|
* What an app adds to `.brydio/app.json` to be more than a bundle of servers
|
|
@@ -155,6 +155,8 @@ const collectionSchema = z.object({
|
|
|
155
155
|
anonymous: anonymousSchema.optional(),
|
|
156
156
|
/** One of its string[] fields: the user ids who alone may read a record, when it names any (P16). */
|
|
157
157
|
readers: readersSchema.optional(),
|
|
158
|
+
/** One of its string[] fields: the user ids who alone may change a record, when it names any (DW06). */
|
|
159
|
+
editors: editorsSchema.optional(),
|
|
158
160
|
});
|
|
159
161
|
const screenSchema = z.object({
|
|
160
162
|
/**
|
|
@@ -200,7 +202,12 @@ const customToolSchema = z.object({
|
|
|
200
202
|
});
|
|
201
203
|
const toolsSchema = z.object({
|
|
202
204
|
/** Off only when the app supplies every tool itself (A3-F08). */
|
|
203
|
-
|
|
205
|
+
/**
|
|
206
|
+
* Off only when the app supplies every tool itself. `"read"` keeps the
|
|
207
|
+
* generated reads (get, list, search) and leaves every write to the app's
|
|
208
|
+
* own tools, where its rules about who may change what live (DW06).
|
|
209
|
+
*/
|
|
210
|
+
generated: z.union([z.boolean(), z.literal('read')]).optional(),
|
|
204
211
|
custom: z.array(customToolSchema).max(MAX_CUSTOM_TOOLS).optional(),
|
|
205
212
|
});
|
|
206
213
|
/** Most secrets one app may declare (ADR-A24). */
|
|
@@ -395,6 +402,7 @@ export function dataProblems(additions, options = {}) {
|
|
|
395
402
|
// A collection whose records name their readers names a list of user ids to keep them in (P16).
|
|
396
403
|
for (const [collection, declared] of collections) {
|
|
397
404
|
problems.push(...readersProblems(collection, declared));
|
|
405
|
+
problems.push(...editorsProblems(collection, declared));
|
|
398
406
|
}
|
|
399
407
|
// What the app keeps must be what it asks to keep (A3-F06-S01): a
|
|
400
408
|
// collection the grants leave out would be data a workspace never agreed to
|
|
@@ -497,7 +505,7 @@ export function dataProblems(additions, options = {}) {
|
|
|
497
505
|
function folderProblems(additions) {
|
|
498
506
|
const problems = [];
|
|
499
507
|
const custom = new Map((additions.tools?.custom ?? []).map(tool => [tool.name, tool]));
|
|
500
|
-
const generatedWrites = additions.tools?.generated === false
|
|
508
|
+
const generatedWrites = additions.tools?.generated === false || additions.tools?.generated === 'read'
|
|
501
509
|
? new Set()
|
|
502
510
|
: new Set(Object.entries(additions.data ?? {}).flatMap(([name, declared]) => {
|
|
503
511
|
const label = labelOf(name, declared.label);
|
|
@@ -558,15 +566,11 @@ function customToolProblems(additions, options) {
|
|
|
558
566
|
: new Set(Object.entries(additions.data ?? {}).flatMap(([name, declared]) => {
|
|
559
567
|
const label = labelOf(name, declared.label);
|
|
560
568
|
const plural = `${label}s`;
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
`delete_${label}`,
|
|
566
|
-
`list_${plural}`,
|
|
567
|
-
`search_${plural}`,
|
|
568
|
-
`batch_${plural}`,
|
|
569
|
-
];
|
|
569
|
+
const reads = [`get_${label}`, `list_${plural}`, `search_${plural}`];
|
|
570
|
+
// With only reads generated, the write names are the app's to use.
|
|
571
|
+
return additions.tools?.generated === 'read'
|
|
572
|
+
? reads
|
|
573
|
+
: [...reads, `create_${label}`, `update_${label}`, `delete_${label}`, `batch_${plural}`];
|
|
570
574
|
}));
|
|
571
575
|
const seen = new Set();
|
|
572
576
|
const granted = additions.grants?.tools ?? [];
|
|
@@ -727,7 +731,13 @@ export function collectionsOf(manifest) {
|
|
|
727
731
|
const tableField = declared.openSchema?.table ?? owner?.flag.table;
|
|
728
732
|
const fields = Object.fromEntries(Object.entries(declared.schema).map(([field, raw]) => {
|
|
729
733
|
const type = parseFieldType(raw);
|
|
730
|
-
|
|
734
|
+
if (field === tableField)
|
|
735
|
+
return [field, { ...type, plain: true }];
|
|
736
|
+
// A record's readers or editors may name a whole team (P16, DW06).
|
|
737
|
+
return [
|
|
738
|
+
field,
|
|
739
|
+
(field === declared.readers || field === declared.editors) && type.kind === 'string[]' ? { ...type, maxEntries: READERS_LIMIT } : type,
|
|
740
|
+
];
|
|
731
741
|
}));
|
|
732
742
|
const label = labelOf(name, declared.label);
|
|
733
743
|
const names = Object.keys(fields);
|
|
@@ -746,6 +756,7 @@ export function collectionsOf(manifest) {
|
|
|
746
756
|
...(owner ? { definesFieldsOf: owner.name } : {}),
|
|
747
757
|
...(declared.anonymous ? { anonymous: anonymousSpec(declared.anonymous) } : {}),
|
|
748
758
|
...(declared.readers ? { readers: declared.readers } : {}),
|
|
759
|
+
...(declared.editors ? { editors: declared.editors } : {}),
|
|
749
760
|
};
|
|
750
761
|
});
|
|
751
762
|
}
|