@brydio/manifest 0.1.0-alpha.36 → 0.1.0-alpha.37
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/grants.d.ts +10 -2
- package/src/grants.js +24 -2
- package/src/index.d.ts +1 -1
- package/src/index.js +1 -1
- package/src/schema.d.ts +17 -1
- package/src/schema.js +44 -2
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.37",
|
|
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": "c0364642e34a602b75477af366aa61582e5598f7",
|
|
14
14
|
"exports": {
|
|
15
15
|
".": {
|
|
16
16
|
"types": "./src/index.d.ts",
|
package/src/grants.d.ts
CHANGED
|
@@ -6,8 +6,16 @@
|
|
|
6
6
|
* `apps/api/src/apps/manifest/grants.ts`, and the `grant_unknown` sentence in
|
|
7
7
|
* `apps/api/src/apps/publishing/app-publish.service.ts`.
|
|
8
8
|
*/
|
|
9
|
-
/**
|
|
10
|
-
|
|
9
|
+
/**
|
|
10
|
+
* The host grants with a name of their own. A connection's is `connection:<name>`.
|
|
11
|
+
*
|
|
12
|
+
* `chats` and `chat` differ: `chats` is the typed Brydio API's reach into the
|
|
13
|
+
* person's assistant conversations; `chat` is a handler's `chat.post`, the
|
|
14
|
+
* app posting one of its cards into a workspace room (FO03). `directory` is
|
|
15
|
+
* a handler's `directory.*` and a screen's `useProfile` (FO02); `webhooks`
|
|
16
|
+
* is a handler's `webhooks.send` (FO07).
|
|
17
|
+
*/
|
|
18
|
+
export declare const HOST_CAPABILITIES: readonly ["navigate", "message", "members", "projects", "files", "chats", "model", "secrets", "notify", "approvals", "chat", "directory", "webhooks"];
|
|
11
19
|
/** The older name for `HOST_CAPABILITIES`, kept for what already imports it. */
|
|
12
20
|
export declare const KNOWN_HOST_GRANTS: readonly string[];
|
|
13
21
|
/** True for a host grant Brydio knows how to honour: a named capability, or one connection. */
|
package/src/grants.js
CHANGED
|
@@ -7,8 +7,30 @@
|
|
|
7
7
|
* `apps/api/src/apps/publishing/app-publish.service.ts`.
|
|
8
8
|
*/
|
|
9
9
|
import { BUNDLE_MANIFEST } from "./bundle.js";
|
|
10
|
-
/**
|
|
11
|
-
|
|
10
|
+
/**
|
|
11
|
+
* The host grants with a name of their own. A connection's is `connection:<name>`.
|
|
12
|
+
*
|
|
13
|
+
* `chats` and `chat` differ: `chats` is the typed Brydio API's reach into the
|
|
14
|
+
* person's assistant conversations; `chat` is a handler's `chat.post`, the
|
|
15
|
+
* app posting one of its cards into a workspace room (FO03). `directory` is
|
|
16
|
+
* a handler's `directory.*` and a screen's `useProfile` (FO02); `webhooks`
|
|
17
|
+
* is a handler's `webhooks.send` (FO07).
|
|
18
|
+
*/
|
|
19
|
+
export const HOST_CAPABILITIES = [
|
|
20
|
+
'navigate',
|
|
21
|
+
'message',
|
|
22
|
+
'members',
|
|
23
|
+
'projects',
|
|
24
|
+
'files',
|
|
25
|
+
'chats',
|
|
26
|
+
'model',
|
|
27
|
+
'secrets',
|
|
28
|
+
'notify',
|
|
29
|
+
'approvals',
|
|
30
|
+
'chat',
|
|
31
|
+
'directory',
|
|
32
|
+
'webhooks',
|
|
33
|
+
];
|
|
12
34
|
/** The older name for `HOST_CAPABILITIES`, kept for what already imports it. */
|
|
13
35
|
export const KNOWN_HOST_GRANTS = HOST_CAPABILITIES;
|
|
14
36
|
const CONNECTION = /^connection:[a-z0-9][a-z0-9_-]{0,59}$/;
|
package/src/index.d.ts
CHANGED
|
@@ -7,7 +7,7 @@ export { APP_NAME_FORMAT, MANIFEST_LIMITS, SEMVER_FORMAT, baseManifestSchema, br
|
|
|
7
7
|
export { BRAND_FIELDS, BRAND_LIMITS, brandFilePath, brandPathsOf, brandProblems, brandShapeOf, isBrandPath, pngVerdict, svgColours, toolsProblem, type BrandField, type RequirementCode, type RequirementFiles, type RequirementProblem, } from './requirements.js';
|
|
8
8
|
export { BUNDLE_MANIFEST, BUNDLE_MAX_BYTES, BUNDLE_PATH_MAX_CHARS, bundleBytes, bundleHash, brandOf, bundleProblem, codeOf, isBundlePath, isScriptPath, sizeOf, type BundleFiles, type BundleProblem, type BundleProblemCode, } from './bundle.js';
|
|
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
|
-
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';
|
|
10
|
+
export { appManifestSchema, appToolPrefix, collectionOf, crossAppTool, 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
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';
|
package/src/index.js
CHANGED
|
@@ -7,7 +7,7 @@ export { APP_NAME_FORMAT, MANIFEST_LIMITS, SEMVER_FORMAT, baseManifestSchema, br
|
|
|
7
7
|
export { BRAND_FIELDS, BRAND_LIMITS, brandFilePath, brandPathsOf, brandProblems, brandShapeOf, isBrandPath, pngVerdict, svgColours, toolsProblem, } from "./requirements.js";
|
|
8
8
|
export { BUNDLE_MANIFEST, BUNDLE_MAX_BYTES, BUNDLE_PATH_MAX_CHARS, bundleBytes, bundleHash, brandOf, bundleProblem, codeOf, isBundlePath, isScriptPath, sizeOf, } from "./bundle.js";
|
|
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
|
-
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";
|
|
10
|
+
export { appManifestSchema, appToolPrefix, collectionOf, crossAppTool, 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
12
|
export { CONFIRM_LIMITS, confirmEmailProblems, confirmEmailSpec, } from "./confirm-email.js";
|
|
13
13
|
export { ANONYMOUS_LIMITS, ANONYMOUS_WRITER, anonymousProblems, anonymousSpec, } from "./anonymous.js";
|
package/src/schema.d.ts
CHANGED
|
@@ -102,6 +102,7 @@ declare const extensionShape: {
|
|
|
102
102
|
"project-sidebar": "project-sidebar";
|
|
103
103
|
"workspace-sidebar": "workspace-sidebar";
|
|
104
104
|
"public-page": "public-page";
|
|
105
|
+
"chat-card": "chat-card";
|
|
105
106
|
}>;
|
|
106
107
|
screen: z.ZodString;
|
|
107
108
|
sizes: z.ZodOptional<z.ZodArray<z.ZodEnum<{
|
|
@@ -240,7 +241,7 @@ export interface DataProblem {
|
|
|
240
241
|
field?: string;
|
|
241
242
|
message: string;
|
|
242
243
|
}
|
|
243
|
-
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_children_too_deep' | 'placement_create_tool_unknown' | 'placement_create_not_write' | 'secret_name_taken' | 'grant_secrets_missing';
|
|
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';
|
|
244
245
|
type Additions = z.infer<z.ZodObject<typeof extensionShape>>;
|
|
245
246
|
/**
|
|
246
247
|
* Everything wrong with an app's additions that a type cannot say.
|
|
@@ -256,6 +257,17 @@ export interface DataProblemOptions {
|
|
|
256
257
|
retired?: boolean;
|
|
257
258
|
}
|
|
258
259
|
export declare function dataProblems(additions: Additions, options?: DataProblemOptions): DataProblem[];
|
|
260
|
+
/**
|
|
261
|
+
* Another installed app's tool, as `grants.tools` names it (FO07): the name
|
|
262
|
+
* the assistant knows it by, `<slug>__<tool>`. Null for anything else.
|
|
263
|
+
* A handler calls it with `tools.call('<slug>__<tool>', input)`.
|
|
264
|
+
*/
|
|
265
|
+
export declare function crossAppTool(name: string): {
|
|
266
|
+
app: string;
|
|
267
|
+
tool: string;
|
|
268
|
+
} | null;
|
|
269
|
+
/** An app's name as the front of its tools' assistant names: `issue-tracker` → `issue_tracker`. */
|
|
270
|
+
export declare const appToolPrefix: (name: string) => string;
|
|
259
271
|
/** The additions alone, for code that has the rest of the manifest already. */
|
|
260
272
|
export declare const manifestExtensionsSchema: z.ZodObject<{
|
|
261
273
|
placements: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
@@ -267,6 +279,7 @@ export declare const manifestExtensionsSchema: z.ZodObject<{
|
|
|
267
279
|
"project-sidebar": "project-sidebar";
|
|
268
280
|
"workspace-sidebar": "workspace-sidebar";
|
|
269
281
|
"public-page": "public-page";
|
|
282
|
+
"chat-card": "chat-card";
|
|
270
283
|
}>;
|
|
271
284
|
screen: z.ZodString;
|
|
272
285
|
sizes: z.ZodOptional<z.ZodArray<z.ZodEnum<{
|
|
@@ -402,6 +415,7 @@ export declare const storedExtensionsSchema: z.ZodObject<{
|
|
|
402
415
|
"project-sidebar": "project-sidebar";
|
|
403
416
|
"workspace-sidebar": "workspace-sidebar";
|
|
404
417
|
"public-page": "public-page";
|
|
418
|
+
"chat-card": "chat-card";
|
|
405
419
|
}>;
|
|
406
420
|
screen: z.ZodString;
|
|
407
421
|
sizes: z.ZodOptional<z.ZodArray<z.ZodEnum<{
|
|
@@ -572,6 +586,7 @@ export declare const appManifestSchema: z.ZodObject<{
|
|
|
572
586
|
"project-sidebar": "project-sidebar";
|
|
573
587
|
"workspace-sidebar": "workspace-sidebar";
|
|
574
588
|
"public-page": "public-page";
|
|
589
|
+
"chat-card": "chat-card";
|
|
575
590
|
}>;
|
|
576
591
|
screen: z.ZodString;
|
|
577
592
|
sizes: z.ZodOptional<z.ZodArray<z.ZodEnum<{
|
|
@@ -746,6 +761,7 @@ export declare const newAppManifestSchema: z.ZodObject<{
|
|
|
746
761
|
"project-sidebar": "project-sidebar";
|
|
747
762
|
"workspace-sidebar": "workspace-sidebar";
|
|
748
763
|
"public-page": "public-page";
|
|
764
|
+
"chat-card": "chat-card";
|
|
749
765
|
}>;
|
|
750
766
|
screen: z.ZodString;
|
|
751
767
|
sizes: z.ZodOptional<z.ZodArray<z.ZodEnum<{
|
package/src/schema.js
CHANGED
|
@@ -72,8 +72,12 @@ const placementSchema = z.object({
|
|
|
72
72
|
* `children` or `settings` (`placement_public_shape`), and it reaches only
|
|
73
73
|
* collections marked `publicRead` or `publicSubmit` and tools marked
|
|
74
74
|
* `public`.
|
|
75
|
+
* `chat-card`: a screen drawn inside a chat message the app posted with
|
|
76
|
+
* a handler's `chat.post` (FO03). Nobody places it by hand, so it has no
|
|
77
|
+
* `sizes`, `children` or `settings` (`placement_chat_card_shape`); its
|
|
78
|
+
* screen sees `placement.kind === 'chat-card'` and the card's `route`.
|
|
75
79
|
*/
|
|
76
|
-
kind: z.enum(['project-tab', 'project-widget', 'project-sidebar', 'workspace-sidebar', 'home', 'public-page']),
|
|
80
|
+
kind: z.enum(['project-tab', 'project-widget', 'project-sidebar', 'workspace-sidebar', 'home', 'public-page', 'chat-card']),
|
|
77
81
|
screen: z.string().min(1).max(FIELD_LIMITS.nameChars),
|
|
78
82
|
/** For `home` and `project-widget` only, and required there: the sizes the screen draws well at. */
|
|
79
83
|
sizes: z.array(z.enum(HOME_CARD_SIZES)).max(3).optional(),
|
|
@@ -454,6 +458,14 @@ export function dataProblems(additions, options = {}) {
|
|
|
454
458
|
message: `The public page "${placement.screen}" can have no children or settings: it is one screen at its own address.`,
|
|
455
459
|
});
|
|
456
460
|
}
|
|
461
|
+
// A chat card is drawn at the message's own size, in a message nobody
|
|
462
|
+
// places by hand (FO03): nothing to size, configure or list.
|
|
463
|
+
if (placement.kind === 'chat-card' && (placement.sizes?.length || placement.children || placement.settings)) {
|
|
464
|
+
problems.push({
|
|
465
|
+
code: 'placement_chat_card_shape',
|
|
466
|
+
message: `The chat card "${placement.screen}" cannot have sizes, children or settings.`,
|
|
467
|
+
});
|
|
468
|
+
}
|
|
457
469
|
}
|
|
458
470
|
// A public page with nothing public could show a visitor nothing at all.
|
|
459
471
|
if ((additions.placements ?? []).some(placement => placement.kind === 'public-page')) {
|
|
@@ -551,6 +563,20 @@ function customToolProblems(additions, options) {
|
|
|
551
563
|
}));
|
|
552
564
|
const seen = new Set();
|
|
553
565
|
const granted = additions.grants?.tools ?? [];
|
|
566
|
+
// Another app's tool, asked for by its assistant name `<slug>__<tool>`
|
|
567
|
+
// (FO07): never an unknown custom tool of this app, but naming the app
|
|
568
|
+
// itself would be a way round its own grants.
|
|
569
|
+
const name = additions.name;
|
|
570
|
+
const own = typeof name === 'string' ? appToolPrefix(name) : null;
|
|
571
|
+
for (const grant of granted) {
|
|
572
|
+
const qualified = crossAppTool(grant);
|
|
573
|
+
if (qualified && own !== null && qualified.app === own) {
|
|
574
|
+
problems.push({
|
|
575
|
+
code: 'grant_tool_cross_app_self',
|
|
576
|
+
message: `${grant} names this app's own tool: ask for ${qualified.tool} instead.`,
|
|
577
|
+
});
|
|
578
|
+
}
|
|
579
|
+
}
|
|
554
580
|
for (const tool of custom) {
|
|
555
581
|
if (generated.has(tool.name) || seen.has(tool.name)) {
|
|
556
582
|
problems.push({
|
|
@@ -587,6 +613,22 @@ function customToolProblems(additions, options) {
|
|
|
587
613
|
}
|
|
588
614
|
return problems;
|
|
589
615
|
}
|
|
616
|
+
/**
|
|
617
|
+
* Another installed app's tool, as `grants.tools` names it (FO07): the name
|
|
618
|
+
* the assistant knows it by, `<slug>__<tool>`. Null for anything else.
|
|
619
|
+
* A handler calls it with `tools.call('<slug>__<tool>', input)`.
|
|
620
|
+
*/
|
|
621
|
+
export function crossAppTool(name) {
|
|
622
|
+
const at = name.indexOf('__');
|
|
623
|
+
if (at <= 0 || at + 2 >= name.length)
|
|
624
|
+
return null;
|
|
625
|
+
return { app: name.slice(0, at), tool: name.slice(at + 2) };
|
|
626
|
+
}
|
|
627
|
+
/** An app's name as the front of its tools' assistant names: `issue-tracker` → `issue_tracker`. */
|
|
628
|
+
export const appToolPrefix = (name) => name
|
|
629
|
+
.replace(/[^A-Za-z0-9_]+/g, '_')
|
|
630
|
+
.replace(/_+/g, '_')
|
|
631
|
+
.replace(/^_+|_+$/g, '');
|
|
590
632
|
/**
|
|
591
633
|
* Each secret declared once, and asked for (ADR-A24): an app that declares
|
|
592
634
|
* secrets without the `secrets` host grant could never read one.
|
|
@@ -619,7 +661,7 @@ const refuse = (additions, ctx, options = {}) => {
|
|
|
619
661
|
ctx.addIssue({
|
|
620
662
|
code: 'custom',
|
|
621
663
|
message: problem.message,
|
|
622
|
-
path: problem.code === 'grant_tool_missing'
|
|
664
|
+
path: problem.code === 'grant_tool_missing' || problem.code === 'grant_tool_cross_app_self'
|
|
623
665
|
? ['grants', 'tools']
|
|
624
666
|
: problem.code === 'grant_secrets_missing'
|
|
625
667
|
? ['grants', 'host']
|