@diagonal-hq/cli 0.0.0-stage → 0.1.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.
@@ -0,0 +1,72 @@
1
+ // The `Sdk` and `React` globals of page, layout, component, and email files.
2
+ // The same types that the deploy type check uses.
3
+ /// <reference path="./sdk-contract.d.ts" />
4
+ declare namespace React {
5
+ type ReactNode = RenderableChild;
6
+ type ReactElement = any;
7
+ type ComponentType<P = any> = (props: P) => any;
8
+ type FC<P = any> = (props: P & { children?: RenderableChild }) => any;
9
+ type PropsWithChildren<P = unknown> = P & { children?: RenderableChild };
10
+ type CSSProperties = { [property: string]: any };
11
+ type RefObject<T = any> = { current: T };
12
+ type MutableRefObject<T = any> = { current: T };
13
+ type Ref<T = any> = ((instance: T | null) => void) | RefObject<T | null> | null;
14
+ type Dispatch<A = any> = (value: A) => void;
15
+ type SetStateAction<S = any> = S | ((previousState: S) => S);
16
+ type DependencyList = readonly unknown[];
17
+ type ChangeEvent<T = any> = any;
18
+ type FormEvent<T = any> = any;
19
+ type MouseEvent<T = any> = any;
20
+ type KeyboardEvent<T = any> = any;
21
+ type FocusEvent<T = any> = any;
22
+ }
23
+ interface DiagonalReactGlobal {
24
+ useState(initialState: null): [any, React.Dispatch<React.SetStateAction<any>>];
25
+ useState(initialState: never[]): [any[], React.Dispatch<React.SetStateAction<any[]>>];
26
+ useState<S>(initialState: S | (() => S)): [S, React.Dispatch<React.SetStateAction<S>>];
27
+ useState<S = undefined>(): [S | undefined, React.Dispatch<React.SetStateAction<S | undefined>>];
28
+ useEffect(effect: () => any, deps?: React.DependencyList): void;
29
+ useLayoutEffect(effect: () => any, deps?: React.DependencyList): void;
30
+ useMemo<T>(factory: () => T, deps?: React.DependencyList): T;
31
+ useCallback<T extends (...args: any[]) => any>(callback: T, deps?: React.DependencyList): T;
32
+ useRef<T>(initialValue: T): React.RefObject<T>;
33
+ useRef<T>(initialValue: T | null): React.RefObject<T | null>;
34
+ useRef<T>(initialValue: T | undefined): React.RefObject<T | undefined>;
35
+ useReducer<S, A>(reducer: (state: S, action: A) => S, initialState: S): [S, React.Dispatch<A>];
36
+ useReducer<S, A, I>(
37
+ reducer: (state: S, action: A) => S,
38
+ initializerArg: I,
39
+ initializer: (arg: I) => S
40
+ ): [S, React.Dispatch<A>];
41
+ useContext<T = any>(context: unknown): T;
42
+ useId(): string;
43
+ useTransition(): [boolean, (callback: () => void) => void];
44
+ useDeferredValue<T>(value: T): T;
45
+ memo<T>(component: T, arePropsEqual?: (previous: any, next: any) => boolean): T;
46
+ forwardRef<T = any, P = any>(
47
+ render: (props: P, ref: React.Ref<T>) => any
48
+ ): (props: P & { ref?: React.Ref<T> }) => any;
49
+ [member: string]: any;
50
+ }
51
+ declare const React: DiagonalReactGlobal;
52
+ declare namespace JSX {
53
+ type Element = any;
54
+ interface ElementChildrenAttribute { children: {}; }
55
+ interface IntrinsicAttributes { key?: any; }
56
+ interface IntrinsicElements { [name: string]: { [attribute: string]: any; children?: RenderableChild }; }
57
+ }
58
+ type RenderableChild =
59
+ | string
60
+ | number
61
+ | bigint
62
+ | boolean
63
+ | null
64
+ | undefined
65
+ | Iterable<RenderableChild>
66
+ | Promise<RenderableChild>;
67
+
68
+ // The editor does not know the workspace components, so any Sdk.Custom name is allowed.
69
+ declare module "diagonal:sdk-internal" {
70
+ interface CustomComponentRegistry { [key: string]: SdkComponent }
71
+ }
72
+
@@ -0,0 +1,274 @@
1
+ import './globals';
2
+
3
+ // The configuration helpers of file-based workspaces. The workspace build
4
+ // reads these calls as static data. index.js implements each define* function
5
+ // as an identity function and each field builder as `{ $field, ...options }`.
6
+
7
+ export type StaticValue =
8
+ | string
9
+ | number
10
+ | boolean
11
+ | null
12
+ | readonly StaticValue[]
13
+ | { readonly [key: string]: StaticValue };
14
+
15
+ export type ConfigDefinition = {
16
+ name: string;
17
+ brand?: { readonly [key: string]: StaticValue };
18
+ settings?: { readonly [key: string]: StaticValue };
19
+ };
20
+
21
+ export declare function defineConfig<const T extends ConfigDefinition>(config: T): T;
22
+
23
+ export type FieldType =
24
+ | 'text'
25
+ | 'email'
26
+ | 'number'
27
+ | 'boolean'
28
+ | 'date'
29
+ | 'datetime'
30
+ | 'phone'
31
+ | 'url'
32
+ | 'select'
33
+ | 'money'
34
+ | 'markdown'
35
+ | 'relation';
36
+
37
+ export type FieldOptions = {
38
+ label?: string;
39
+ description?: string | null;
40
+ required?: boolean;
41
+ unique?: boolean;
42
+ index?: boolean;
43
+ default?: StaticValue;
44
+ [setting: string]: StaticValue | undefined;
45
+ };
46
+
47
+ export type SelectOption = { label: string; value: string };
48
+
49
+ export type SelectFieldOptions = FieldOptions & { options: readonly SelectOption[] };
50
+
51
+ export type RelationType = 'belongs_to' | 'has_many' | 'belongs_to_many';
52
+
53
+ export type RelationFieldOptions = FieldOptions & {
54
+ /** The id or slug of the related resource. */
55
+ resource: string;
56
+ type?: RelationType;
57
+ };
58
+
59
+ export type FieldDefinition<TType extends FieldType = FieldType> = {
60
+ readonly $field: TType;
61
+ };
62
+
63
+ export declare const field: {
64
+ text(options?: FieldOptions): FieldDefinition<'text'>;
65
+ /** Stored as a `text` field. */
66
+ longText(options?: FieldOptions): FieldDefinition<'text'>;
67
+ email(options?: FieldOptions): FieldDefinition<'email'>;
68
+ number(options?: FieldOptions): FieldDefinition<'number'>;
69
+ boolean(options?: FieldOptions): FieldDefinition<'boolean'>;
70
+ date(options?: FieldOptions): FieldDefinition<'date'>;
71
+ datetime(options?: FieldOptions): FieldDefinition<'datetime'>;
72
+ phone(options?: FieldOptions): FieldDefinition<'phone'>;
73
+ url(options?: FieldOptions): FieldDefinition<'url'>;
74
+ select(options: SelectFieldOptions): FieldDefinition<'select'>;
75
+ money(options?: FieldOptions): FieldDefinition<'money'>;
76
+ markdown(options?: FieldOptions): FieldDefinition<'markdown'>;
77
+ relation(options: RelationFieldOptions): FieldDefinition<'relation'>;
78
+ };
79
+
80
+ export type PolicyScope =
81
+ | 'index'
82
+ | 'show'
83
+ | 'count'
84
+ | 'aggregate'
85
+ | 'store'
86
+ | 'storeMany'
87
+ | 'update'
88
+ | 'destroy'
89
+ | 'restore'
90
+ | 'validate';
91
+
92
+ export type PolicyAccess = 'public' | 'protected';
93
+
94
+ export type PolicyDefinition = {
95
+ name: string;
96
+ scope: 'all' | readonly PolicyScope[];
97
+ access: PolicyAccess;
98
+ description?: string | null;
99
+ };
100
+
101
+ export type IndexDefinition<TKey extends string = string> = {
102
+ fields: readonly TKey[];
103
+ unique?: boolean;
104
+ };
105
+
106
+ export type ResourceDefinition<
107
+ TFields extends Record<string, FieldDefinition> = Record<string, FieldDefinition>
108
+ > = {
109
+ /**
110
+ * A lowercase UUID v7, for example '0199f000-0000-7000-8000-000000000001'.
111
+ * `diagonal check` suggests a new one when the id is not v7. Keep it
112
+ * stable: records and workflows refer to it.
113
+ */
114
+ id: string;
115
+ slug: string;
116
+ name: string;
117
+ singularName: string;
118
+ displayField?: Extract<keyof TFields, string>;
119
+ fields: TFields;
120
+ indexes?: readonly IndexDefinition<Extract<keyof TFields, string>>[];
121
+ policies: readonly PolicyDefinition[];
122
+ };
123
+
124
+ export declare function defineResource<const TFields extends Record<string, FieldDefinition>>(
125
+ resource: ResourceDefinition<TFields>
126
+ ): ResourceDefinition<TFields>;
127
+
128
+ /** A TipTap document whose mentions resolve against the page's bound records. */
129
+ export type PageMetaResolver = { readonly type: string; readonly [key: string]: unknown };
130
+
131
+ export type PageRouteParam = {
132
+ /** The slug or id of the resource whose record the parameter names. */
133
+ resource: string;
134
+ /** The field the parameter matches. Defaults to the record id. */
135
+ lookupField?: string | null;
136
+ /** Relation fields to load with the record. */
137
+ relations?: readonly string[];
138
+ scope?: 'active' | 'activeOrArchived' | 'archived' | null;
139
+ };
140
+
141
+ export type PageAuthorizer =
142
+ | null
143
+ | 'authenticated'
144
+ | {
145
+ /** The slug or id of the resource whose signed-in records may view the page. */
146
+ resource?: string | null;
147
+ /** Permission ids. A viewer needs any one of them. */
148
+ permissions?: readonly string[];
149
+ };
150
+
151
+ export type PageDefinition = {
152
+ /** The URL pattern. Route parameters use `{name}`, for example `/members/{id}`. */
153
+ route: string;
154
+ /** The name in the admin. Defaults to the title. */
155
+ name?: string | null;
156
+ title?: string | PageMetaResolver | null;
157
+ description?: string | PageMetaResolver | null;
158
+ /** A public workspace file id, or a resolver that names one. */
159
+ ogImage?: string | PageMetaResolver | null;
160
+ /** HTML added to the page's `<head>`. */
161
+ headHtml?: string | null;
162
+ /** The file name of a layout in `layouts/`, without the extension. */
163
+ layout?: string | null;
164
+ /** `print` renders without a layout. */
165
+ renderProfile?: 'standard' | 'print';
166
+ /** Whether the sitemap lists the page. Defaults to true, or false for a print page. */
167
+ sitemap?: boolean;
168
+ /** The host of a connected custom domain. A page with a domain serves only there. */
169
+ domain?: string | null;
170
+ /** A paused page does not serve. */
171
+ paused?: boolean;
172
+ authorizer?: PageAuthorizer;
173
+ /** The record each route parameter binds to, keyed by parameter name. */
174
+ params?: Readonly<Record<string, PageRouteParam>>;
175
+ };
176
+
177
+ export type PageComponent = () => unknown;
178
+
179
+ export declare function definePage<const T extends PageDefinition>(
180
+ page: T,
181
+ component: PageComponent
182
+ ): T;
183
+
184
+ export type LayoutProps = { children?: React.ReactNode };
185
+
186
+ export type EmailDefinition = { subject: string };
187
+
188
+ export declare function defineEmail<const T extends EmailDefinition, P>(
189
+ email: T,
190
+ component: (props: P) => unknown
191
+ ): T;
192
+
193
+ export type NavigationItem = { label: string; route: string; icon?: string | null };
194
+
195
+ export type NavigationDefinition = { id: string; items: readonly NavigationItem[] };
196
+
197
+ export declare function defineNavigation<const T extends NavigationDefinition>(navigation: T): T;
198
+
199
+ /** The slug, name, or id of a resource. */
200
+ export type ResourceReference = string;
201
+
202
+ export type WorkflowRunAs =
203
+ | 'system'
204
+ /** Only for request workflows: the signed-in user who calls the route. */
205
+ | 'session'
206
+ | {
207
+ resource: ResourceReference;
208
+ /** The lowercase UUID of the identity record. */
209
+ record: string;
210
+ };
211
+
212
+ export type RequestTrigger = {
213
+ type: 'request';
214
+ /** The route. Route parameters use `{name}`, for example `/api/members/{id}`. */
215
+ path: string;
216
+ method?: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
217
+ };
218
+
219
+ export type RecordEventTrigger = {
220
+ type: 'recordEvent';
221
+ resource: ResourceReference;
222
+ event: 'created' | 'updated' | 'deleted' | 'restored' | 'saved';
223
+ filters?: StaticValue;
224
+ };
225
+
226
+ export type RecordLifecycleTrigger = {
227
+ type: 'recordLifecycle';
228
+ resource: ResourceReference;
229
+ hook: 'creating' | 'updating' | 'saving' | 'deleting' | 'restoring';
230
+ };
231
+
232
+ export type RecordScheduleTrigger = {
233
+ type: 'recordSchedule';
234
+ resource: ResourceReference;
235
+ /** The key of a date or datetime field of the resource. */
236
+ dateField: string;
237
+ /** Signed ISO 8601 durations in days, hours, or minutes, for example `'-P1D'` or `'+PT2H'`. */
238
+ offsets: readonly string[];
239
+ };
240
+
241
+ export type ScheduledTrigger = {
242
+ type: 'scheduled';
243
+ /** A cron expression with 5 fields, for example `'0 2 * * *'`. */
244
+ expression: string;
245
+ };
246
+
247
+ export type WorkflowEventTrigger = {
248
+ type: 'workflowEvent';
249
+ /** The path of another workflow, for example `'workflows/welcome.ts'`. */
250
+ workflow: `workflows/${string}.ts`;
251
+ event: 'execution.success' | 'execution.error';
252
+ };
253
+
254
+ export type ManualTrigger = { type: 'manual' };
255
+
256
+ type ConfigBase = { title: string; description?: string | null };
257
+
258
+ /**
259
+ * The `export const config` of a `workflows/<name>.ts` file. The build reads it as static data and
260
+ * never runs it. A background trigger (recordEvent, recordSchedule, scheduled, workflowEvent)
261
+ * needs `runAs`. A request workflow runs as the caller (`'session'`) by default.
262
+ */
263
+ export type WorkflowConfig =
264
+ | (ConfigBase & {
265
+ trigger: RequestTrigger;
266
+ runAs?: WorkflowRunAs;
267
+ authorizer?: 'authenticated';
268
+ })
269
+ | (ConfigBase & {
270
+ trigger: RecordEventTrigger | RecordScheduleTrigger | ScheduledTrigger | WorkflowEventTrigger;
271
+ runAs: Exclude<WorkflowRunAs, 'session'>;
272
+ })
273
+ | (ConfigBase & { trigger: ManualTrigger; runAs?: Exclude<WorkflowRunAs, 'session'> })
274
+ | (ConfigBase & { trigger: RecordLifecycleTrigger });
package/dist/index.js ADDED
@@ -0,0 +1,33 @@
1
+ // The configuration helpers. The workspace build reads these calls as static
2
+ // data, so the helpers only return their argument. A Vite build or a Node
3
+ // script that imports a configuration file runs them.
4
+ //
5
+ // Each function must not refer to anything outside its own body:
6
+ // scripts/lib/runtime-build/app-modules.mjs copies their source into the
7
+ // workspace modules that a page or a workflow imports.
8
+
9
+ export const defineConfig = (config) => config;
10
+
11
+ export const defineResource = (resource) => resource;
12
+
13
+ export const definePage = (page, _component) => page;
14
+
15
+ export const defineEmail = (email, _component) => email;
16
+
17
+ export const defineNavigation = (navigation) => navigation;
18
+
19
+ export const field = {
20
+ text: (options = {}) => ({ $field: 'text', ...options }),
21
+ longText: (options = {}) => ({ $field: 'text', ...options }),
22
+ email: (options = {}) => ({ $field: 'email', ...options }),
23
+ number: (options = {}) => ({ $field: 'number', ...options }),
24
+ boolean: (options = {}) => ({ $field: 'boolean', ...options }),
25
+ date: (options = {}) => ({ $field: 'date', ...options }),
26
+ datetime: (options = {}) => ({ $field: 'datetime', ...options }),
27
+ phone: (options = {}) => ({ $field: 'phone', ...options }),
28
+ url: (options = {}) => ({ $field: 'url', ...options }),
29
+ select: (options) => ({ $field: 'select', ...options }),
30
+ money: (options = {}) => ({ $field: 'money', ...options }),
31
+ markdown: (options = {}) => ({ $field: 'markdown', ...options }),
32
+ relation: (options) => ({ $field: 'relation', ...options })
33
+ };