@diagonal-hq/cli 0.2.0 → 0.3.0
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/README.md +43 -11
- package/dist/cli.mjs +468 -376
- package/dist/fields.generated.d.ts +1 -1
- package/dist/index.d.ts +19 -14
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -49,15 +49,46 @@ The build reads each file with a parser. It runs only the workflow tests.
|
|
|
49
49
|
|
|
50
50
|
- `defineConfig`, `defineResource`, `defineNavigation`, and the first argument of `definePage` must be static literals. The `field.<type>({...})` calls are the only calls that the build accepts in this data.
|
|
51
51
|
- Each `resources/*.ts` file goes through the resource checks in [Resources and fields](#resources-and-fields).
|
|
52
|
-
- The build removes imports from `@diagonal-hq/cli`. Pages, layouts, components, and workflow scripts can import workspace files through `@app/`. Workflow scripts can also import `@diagonal/workflow` and `@diagonal/workflow/workspace`. Other external imports in these source files are rejected.
|
|
52
|
+
- The build removes imports from `@diagonal-hq/cli`. Pages, layouts, components, and workflow scripts can import workspace files through `@app/`. A resource file can import only `@app/generated/resources`. Workflow scripts can also import `@diagonal/workflow` and `@diagonal/workflow/workspace`. Other external imports in these source files are rejected.
|
|
53
53
|
- The second argument of `definePage`, and the default export of a layout or component file, must be one function.
|
|
54
|
-
- Pages, layouts, and components go through the page lint gate: syntax, scope, the component return shape, and a type check against the page Sdk contract and the workspace resources. A page
|
|
55
|
-
- Each `workflows/<name>.ts` is a script workflow. It has one `export const config = { title, trigger, runAs? } satisfies WorkflowConfig` and one `export default defineWorkflow(...)`. The config must be a static literal. The build validates it: the trigger fields, the resources (
|
|
54
|
+
- Pages, layouts, and components go through the page lint gate: syntax, scope, the component return shape, and a type check against the page Sdk contract and the workspace resources. A page names a resource by slug, for example `Sdk.resource(Resources.Members)`.
|
|
55
|
+
- Each `workflows/<name>.ts` is a script workflow. It has one `export const config = { title, trigger, runAs? } satisfies WorkflowConfig` and one `export default defineWorkflow(...)`. The config must be a static literal. The build validates it: the trigger fields, the resources (see [Resource references](#resource-references)), the workflow paths of `workflowEvent` triggers, and the run-as rules. A `recordEvent`, `recordSchedule`, `scheduled`, or `workflowEvent` workflow needs `runAs`.
|
|
56
56
|
- The build removes the config and the `@diagonal-hq/cli` imports from the workflow, with spaces in their place, and compiles the rest with the workflow script compiler against the workspace resources. A compiler error has the line and column of the workflow file.
|
|
57
|
-
- `workflows/<name>.test.ts` tests `workflows/<name>.ts` with `node:test` and `node:assert`. It imports the workflow, `runWorkflow` and `MockTransport` from `@diagonal/workflow/testing`, and
|
|
58
|
-
- The build ignores the files at the root other than `diagonal.config.ts`, dotfiles, and `node_modules`.
|
|
57
|
+
- `workflows/<name>.test.ts` tests `workflows/<name>.ts` with `node:test` and `node:assert`. It imports the workflow, `runWorkflow` and `MockTransport` from `@diagonal/workflow/testing`, and `Resources` from `@app/generated/resources`. The build transpiles the test and the workflows and runs them in one Node process. The process can read only its own temporary directory, cannot start processes, and has an empty environment. Each failing test is an error at the test. A test file without a workflow is an error.
|
|
58
|
+
- The build ignores the files at the root other than `diagonal.config.ts`, dotfiles, and `node_modules`. It does not build the files in `generated/`, but it checks `generated/resources.ts`.
|
|
59
|
+
|
|
59
60
|
- The build does not read `notifications/` or `email-layouts/` yet. The editor type-checks the shape of their definitions. See [Notifications](#notifications).
|
|
60
61
|
|
|
62
|
+
## Resource references
|
|
63
|
+
|
|
64
|
+
The file name is the resource slug: `resources/job-postings.ts` declares the resource `job-postings`. A file name must start with a lowercase letter and contain only a-z, 0-9, `_`, and `-`. A resource has no `id` in source. The platform derives the stored id from the workspace and the slug, so each branch and environment has its own ids.
|
|
65
|
+
|
|
66
|
+
`diagonal check` writes `generated/resources.ts` from the `resources/` folder. Commit the file:
|
|
67
|
+
|
|
68
|
+
```ts
|
|
69
|
+
// AUTO-GENERATED by `diagonal check`. Do not edit.
|
|
70
|
+
export const Resources = {
|
|
71
|
+
JobPostings: 'job-postings',
|
|
72
|
+
User: 'user'
|
|
73
|
+
} as const;
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Each property is the studly case of a slug, and its value is the slug. A deploy fails when the file does not match the folder, and the error gives the expected content. Two slugs with the same name, such as `job-postings` and `job_postings`, are an error.
|
|
77
|
+
|
|
78
|
+
Refer to a resource with `Resources.<Name>` from `@app/generated/resources` everywhere: `field.relation({ resource: Resources.User })`, a workflow `trigger.resource` and `runAs.resource`, a page `authorizer.resource` and `params.<name>.resource`, `Sdk.resource(Resources.User)`, `data.search({ resource: Resources.User })`, and `v.record(Resources.User)`. The build reads `Resources.<Name>` like a literal. An unknown name is an error at its location, and the editor reports it too.
|
|
79
|
+
|
|
80
|
+
A slug string, such as `resource: 'user'`, builds with a warning. A resource id, a resource name, a singular name, or a slug without a file is an error.
|
|
81
|
+
|
|
82
|
+
The platform provisions some resources itself, such as the staff resource, the roles, and the permissions. A new repository has a reference file for each one, for example `resources/roles.ts`:
|
|
83
|
+
|
|
84
|
+
```ts
|
|
85
|
+
import { defineResource } from '@diagonal-hq/cli';
|
|
86
|
+
|
|
87
|
+
export default defineResource({ platform: true });
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
The file gives the resource its `Resources` entry, so source names it like any other resource. The platform owns its fields, and the deploy does not change it. The build does not know its fields, so a data call types its records loosely, and a relation to it cannot declare an inverse. A deploy fails when the workspace has no platform resource with the slug of the file.
|
|
91
|
+
|
|
61
92
|
## Notifications
|
|
62
93
|
|
|
63
94
|
A notification has a type, one or more templates, and optionally an email layout. Each definition is a static literal. Its Liquid is inline text or a `.liquid` file next to it, written as `{ file: './standard.email.liquid' }`.
|
|
@@ -82,15 +113,16 @@ email-layouts/
|
|
|
82
113
|
The type sets the recipient resource, the variables, and the default template. The recipient resource maps the email and phone fields. A type without `recipient` sends to email addresses. A variable uses a `field` builder other than `field.relation`, or `{ resource }` for one record of a resource. Templates read a record variable's fields, for example `{{ first_event.name }}`.
|
|
83
114
|
|
|
84
115
|
```ts
|
|
116
|
+
import { Resources } from '@app/generated/resources';
|
|
85
117
|
import { defineNotificationType, field } from '@diagonal-hq/cli';
|
|
86
118
|
|
|
87
119
|
export default defineNotificationType({
|
|
88
120
|
key: 'member-welcome',
|
|
89
121
|
name: 'Member welcome',
|
|
90
|
-
recipient: { resource:
|
|
122
|
+
recipient: { resource: Resources.Members },
|
|
91
123
|
variables: {
|
|
92
124
|
joined_on: field.date({ label: 'Joined on', required: true }),
|
|
93
|
-
first_event: { resource:
|
|
125
|
+
first_event: { resource: Resources.Events, label: 'First event' }
|
|
94
126
|
},
|
|
95
127
|
defaultTemplate: 'standard'
|
|
96
128
|
});
|
|
@@ -157,7 +189,7 @@ Fixtures are sample data for previews. They are not tests, and they are never se
|
|
|
157
189
|
|
|
158
190
|
## Resources and fields
|
|
159
191
|
|
|
160
|
-
`defineResource` takes `
|
|
192
|
+
`defineResource` takes `name`, `singularName`, `displayField`, `searchableFields`, `fieldOrder`, `fields`, `indexes`, `embeddings`, `dataApi`, `static`, `policies`, and `capabilities`. The build refuses an unknown property, an unknown setting, and a duplicate key, and gives the line and column. It does not remove anything without a diagnostic.
|
|
161
193
|
|
|
162
194
|
- `searchableFields` lists the field keys and relation paths that record search matches, for example `['title', 'author.full_name']`. The build checks each path against the related resources. Without it, search matches the display field and every text and markdown field.
|
|
163
195
|
- `fieldOrder` lists every field key once. It sets the order of the fields in forms and tables. The default is the order of `fields`.
|
|
@@ -165,7 +197,7 @@ Fixtures are sample data for previews. They are not tests, and they are never se
|
|
|
165
197
|
- `dataApi.endpoints` turns off data API endpoints, for example `{ delete_many: { enabled: false } }`. An omitted endpoint stays enabled.
|
|
166
198
|
- `static: true` refuses normal record writes to the resource.
|
|
167
199
|
|
|
168
|
-
There is one `field.<type>` builder for each resource field type, in camel case: `field.eventSchedule()` stores an `event_schedule` field. `field.file()` stores a `file_reference` field, and `field.longText()` stores a `text` field. `fields.generated.
|
|
200
|
+
There is one `field.<type>` builder for each resource field type, in camel case: `field.eventSchedule()` stores an `event_schedule` field. `field.file()` stores a `file_reference` field, and `field.longText()` stores a `text` field. `fields.generated.d.ts` declares the builders, `fields.generated.js` implements them, and `field-catalog.json` lists their settings. `npm run generate-types` generates the three files from the platform field types. Do not edit them.
|
|
169
201
|
|
|
170
202
|
A builder takes these options:
|
|
171
203
|
|
|
@@ -176,7 +208,7 @@ A builder takes these options:
|
|
|
176
208
|
- `getter`: a resolver tree that transforms the stored value when it is read.
|
|
177
209
|
- `setter`: a resolver tree that computes the stored value before each create and update. `setterWhenEmpty: true` runs it only when the value is null or empty.
|
|
178
210
|
|
|
179
|
-
`field.select` takes `options` and `field.relation` takes `resource` (
|
|
211
|
+
`field.select` takes `options` and `field.relation` takes `resource` (`Resources.<Name>`) and `type` as options. Other settings go in `settings`. A setting that an option writes, such as `settings.unique`, is refused. A relation can declare `settings.inverse: { key, label?, settings? }`. The build then adds the inverse relation field to the target resource. A target that declares a field with the same key is an error.
|
|
180
212
|
|
|
181
213
|
The platform deploy validates every field again with the rules of the resource builder, so a value that the build cannot check, such as an unknown guard rule, fails the deploy with the file path.
|
|
182
214
|
|
|
@@ -234,7 +266,7 @@ Add a `tsconfig.json` with `"types": ["@diagonal-hq/cli", "node"]` and `@types/n
|
|
|
234
266
|
}
|
|
235
267
|
```
|
|
236
268
|
|
|
237
|
-
In the editor,
|
|
269
|
+
In the editor, a data call takes any resource string, and record data is untyped. `Resources` from `@app/generated/resources` catches a wrong resource name. `diagonal check` compiles against the workspace resources and their fields. In a file-based workspace, `@diagonal/workflow/workspace` exports no `resources` map.
|
|
238
270
|
|
|
239
271
|
`globals.d.ts` at the package root has loose `Sdk` and `React` types. The source `index.d.ts` uses them before the package is built.
|
|
240
272
|
|