@ankhorage/contracts 1.7.0 → 1.9.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.
Files changed (63) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/bindings.d.ts +38 -0
  3. package/dist/bindings.d.ts.map +1 -0
  4. package/dist/bindings.js +2 -0
  5. package/dist/bindings.js.map +1 -0
  6. package/dist/data/diagnostics.d.ts +22 -0
  7. package/dist/data/diagnostics.d.ts.map +1 -0
  8. package/dist/data/diagnostics.js +2 -0
  9. package/dist/data/diagnostics.js.map +1 -0
  10. package/dist/data/endpoints.d.ts +18 -0
  11. package/dist/data/endpoints.d.ts.map +1 -0
  12. package/dist/data/endpoints.js +2 -0
  13. package/dist/data/endpoints.js.map +1 -0
  14. package/dist/data/ids.d.ts +7 -0
  15. package/dist/data/ids.d.ts.map +1 -0
  16. package/dist/data/ids.js +2 -0
  17. package/dist/data/ids.js.map +1 -0
  18. package/dist/data/index.d.ts +9 -0
  19. package/dist/data/index.d.ts.map +1 -0
  20. package/dist/data/index.js +9 -0
  21. package/dist/data/index.js.map +1 -0
  22. package/dist/data/operations.d.ts +49 -0
  23. package/dist/data/operations.d.ts.map +1 -0
  24. package/dist/data/operations.js +2 -0
  25. package/dist/data/operations.js.map +1 -0
  26. package/dist/data/refs.d.ts +18 -0
  27. package/dist/data/refs.d.ts.map +1 -0
  28. package/dist/data/refs.js +2 -0
  29. package/dist/data/refs.js.map +1 -0
  30. package/dist/data/schemas.d.ts +34 -0
  31. package/dist/data/schemas.d.ts.map +1 -0
  32. package/dist/data/schemas.js +2 -0
  33. package/dist/data/schemas.js.map +1 -0
  34. package/dist/data/sources.d.ts +57 -0
  35. package/dist/data/sources.d.ts.map +1 -0
  36. package/dist/data/sources.js +2 -0
  37. package/dist/data/sources.js.map +1 -0
  38. package/dist/data/values.d.ts +5 -0
  39. package/dist/data/values.d.ts.map +1 -0
  40. package/dist/data/values.js +2 -0
  41. package/dist/data/values.js.map +1 -0
  42. package/dist/index.d.ts +2 -0
  43. package/dist/index.d.ts.map +1 -1
  44. package/dist/index.js +2 -0
  45. package/dist/index.js.map +1 -1
  46. package/dist/types.d.ts +2 -0
  47. package/dist/types.d.ts.map +1 -1
  48. package/dist/types.js.map +1 -1
  49. package/package.json +9 -1
  50. package/src/bindings.test.ts +96 -0
  51. package/src/bindings.ts +54 -0
  52. package/src/data/data.test.ts +305 -0
  53. package/src/data/diagnostics.ts +41 -0
  54. package/src/data/endpoints.ts +20 -0
  55. package/src/data/ids.ts +6 -0
  56. package/src/data/index.ts +8 -0
  57. package/src/data/operations.ts +66 -0
  58. package/src/data/refs.ts +21 -0
  59. package/src/data/schemas.ts +46 -0
  60. package/src/data/sources.ts +72 -0
  61. package/src/data/values.ts +11 -0
  62. package/src/index.ts +2 -0
  63. package/src/types.ts +2 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ankhorage/contracts",
3
- "version": "1.7.0",
3
+ "version": "1.9.0",
4
4
  "main": "./dist/index.js",
5
5
  "dependencies": {
6
6
  "@ankhorage/color-theory": "^0.0.7"
@@ -21,6 +21,14 @@
21
21
  "types": "./dist/auth.d.ts",
22
22
  "default": "./dist/auth.js"
23
23
  },
24
+ "./bindings": {
25
+ "types": "./dist/bindings.d.ts",
26
+ "default": "./dist/bindings.js"
27
+ },
28
+ "./data": {
29
+ "types": "./dist/data/index.d.ts",
30
+ "default": "./dist/data/index.js"
31
+ },
24
32
  "./db": {
25
33
  "types": "./dist/db.d.ts",
26
34
  "default": "./dist/db.js"
@@ -0,0 +1,96 @@
1
+ import { describe, expect, it } from 'bun:test';
2
+
3
+ import type {
4
+ BindingValueSource,
5
+ DbCollectionQueryBinding,
6
+ NodePropBinding,
7
+ UiNode,
8
+ } from './index';
9
+
10
+ describe('binding contracts', () => {
11
+ it('serializes a node prop bound to state', () => {
12
+ const binding: NodePropBinding = {
13
+ prop: 'value',
14
+ valueFrom: {
15
+ source: 'state',
16
+ path: 'forms.contact.values.firstname',
17
+ },
18
+ transform: 'trim',
19
+ };
20
+ const node: UiNode = {
21
+ id: 'firstname-input',
22
+ type: 'Input',
23
+ props: {
24
+ label: 'Firstname',
25
+ },
26
+ bindings: [binding],
27
+ };
28
+
29
+ expect(JSON.parse(JSON.stringify(node))).toEqual(node);
30
+ expect(node.bindings?.[0]?.valueFrom.source).toBe('state');
31
+ });
32
+
33
+ it('serializes a node prop bound to a DB collection query', () => {
34
+ const query: DbCollectionQueryBinding = {
35
+ collection: 'posts',
36
+ schema: 'public',
37
+ columns: ['id', 'title', 'published'],
38
+ filters: [{ field: 'published', operator: 'eq', value: true }],
39
+ sort: [{ field: 'created_at', direction: 'desc' }],
40
+ page: { limit: 10, offset: 0 },
41
+ refresh: 'live',
42
+ };
43
+ const node: UiNode = {
44
+ id: 'posts-list',
45
+ type: 'CollectionList',
46
+ bindings: [
47
+ {
48
+ prop: 'items',
49
+ valueFrom: {
50
+ source: 'db.collection',
51
+ query,
52
+ },
53
+ },
54
+ ],
55
+ };
56
+
57
+ expect(JSON.parse(JSON.stringify(node))).toEqual(node);
58
+ expect(node.bindings?.[0]?.valueFrom.source).toBe('db.collection');
59
+ });
60
+
61
+ it('supports literal, context, and event value sources', () => {
62
+ const sources: readonly BindingValueSource[] = [
63
+ { source: 'literal', value: { fallback: 'Untitled' } },
64
+ { source: 'context', path: 'auth.user.displayName' },
65
+ { source: 'event', path: 'payload.values.search' },
66
+ ];
67
+
68
+ expect(JSON.parse(JSON.stringify(sources))).toEqual(sources);
69
+ expect(sources.map((source) => source.source)).toEqual(['literal', 'context', 'event']);
70
+ });
71
+
72
+ it('supports static, focus, poll, and live refresh intent', () => {
73
+ const refreshModes = ['static', 'focus', 'poll', 'live'] as const;
74
+ const bindings = refreshModes.map(
75
+ (refresh): NodePropBinding => ({
76
+ prop: `items.${refresh}`,
77
+ valueFrom: {
78
+ source: 'db.collection',
79
+ query: {
80
+ collection: 'posts',
81
+ refresh,
82
+ pollIntervalMs: refresh === 'poll' ? 30_000 : undefined,
83
+ },
84
+ },
85
+ }),
86
+ );
87
+
88
+ expect(JSON.parse(JSON.stringify(bindings))).toEqual(bindings);
89
+ expect(bindings.map((binding) => binding.valueFrom.source)).toEqual([
90
+ 'db.collection',
91
+ 'db.collection',
92
+ 'db.collection',
93
+ 'db.collection',
94
+ ]);
95
+ });
96
+ });
@@ -0,0 +1,54 @@
1
+ import type { DbFilter, DbPage, DbSort } from './db';
2
+
3
+ export type BindingValue =
4
+ | string
5
+ | number
6
+ | boolean
7
+ | null
8
+ | readonly BindingValue[]
9
+ | {
10
+ readonly [key: string]: BindingValue;
11
+ };
12
+
13
+ export type BindingRefreshMode = 'focus' | 'live' | 'poll' | 'static';
14
+
15
+ export interface DbCollectionQueryBinding {
16
+ readonly collection: string;
17
+ readonly schema?: string;
18
+ readonly columns?: readonly string[];
19
+ readonly filters?: readonly DbFilter[];
20
+ readonly sort?: readonly DbSort[];
21
+ readonly page?: DbPage;
22
+ readonly refresh?: BindingRefreshMode;
23
+ readonly pollIntervalMs?: number;
24
+ }
25
+
26
+ export type BindingValueSource =
27
+ | {
28
+ readonly source: 'context';
29
+ readonly path: string;
30
+ }
31
+ | {
32
+ readonly source: 'db.collection';
33
+ readonly query: DbCollectionQueryBinding;
34
+ }
35
+ | {
36
+ readonly source: 'event';
37
+ readonly path: string;
38
+ }
39
+ | {
40
+ readonly source: 'literal';
41
+ readonly value: BindingValue;
42
+ }
43
+ | {
44
+ readonly source: 'state';
45
+ readonly path: string;
46
+ };
47
+
48
+ export type BindingValueTransform = 'lowercase' | 'trim' | 'uppercase';
49
+
50
+ export interface NodePropBinding {
51
+ readonly prop: string;
52
+ readonly valueFrom: BindingValueSource;
53
+ readonly transform?: BindingValueTransform | readonly BindingValueTransform[];
54
+ }
@@ -0,0 +1,305 @@
1
+ import { describe, expect, it } from 'bun:test';
2
+
3
+ import type {
4
+ DataSourceConfig,
5
+ DataSourceDiagnostic,
6
+ GraphQlDataSourceConfig,
7
+ ManagedApiDataSourceConfig,
8
+ OpenApiDataSourceConfig,
9
+ RestDataSourceConfig,
10
+ } from './index';
11
+
12
+ function assertSerializable<TValue>(value: TValue): void {
13
+ expect(JSON.parse(JSON.stringify(value))).toEqual(value);
14
+ }
15
+
16
+ describe('data source contracts', () => {
17
+ it('serializes a manual REST data source with read and create operations', () => {
18
+ const source: RestDataSourceConfig = {
19
+ id: 'cms-rest',
20
+ kind: 'rest',
21
+ name: 'CMS REST API',
22
+ baseUrl: 'https://cms.example.com',
23
+ credential: {
24
+ id: 'cms-api-key',
25
+ kind: 'apiKey',
26
+ label: 'CMS API key',
27
+ },
28
+ schemas: {
29
+ post: {
30
+ type: 'object',
31
+ required: ['id', 'title'],
32
+ properties: {
33
+ id: { type: 'string' },
34
+ title: { type: 'string' },
35
+ published: { type: 'boolean', default: false },
36
+ },
37
+ },
38
+ },
39
+ endpoints: {
40
+ posts: {
41
+ id: 'posts',
42
+ kind: 'http',
43
+ path: '/posts',
44
+ operations: {
45
+ 'posts.list': {
46
+ id: 'posts.list',
47
+ endpointId: 'posts',
48
+ protocol: 'http',
49
+ intent: 'read',
50
+ method: 'GET',
51
+ path: '/posts',
52
+ request: {
53
+ parameters: [
54
+ {
55
+ name: 'limit',
56
+ location: 'query',
57
+ schema: { type: 'integer', default: 20 },
58
+ },
59
+ ],
60
+ },
61
+ response: {
62
+ status: 200,
63
+ schema: {
64
+ type: 'array',
65
+ items: { ref: { id: 'post' } },
66
+ },
67
+ },
68
+ pagination: {
69
+ kind: 'limit-offset',
70
+ limitParameter: 'limit',
71
+ offsetParameter: 'offset',
72
+ },
73
+ },
74
+ 'posts.create': {
75
+ id: 'posts.create',
76
+ endpointId: 'posts',
77
+ protocol: 'http',
78
+ intent: 'create',
79
+ method: 'POST',
80
+ path: '/posts',
81
+ request: {
82
+ contentType: 'application/json',
83
+ schema: { ref: { id: 'post' } },
84
+ },
85
+ response: {
86
+ status: 201,
87
+ schemaRef: { id: 'post' },
88
+ },
89
+ },
90
+ },
91
+ },
92
+ },
93
+ };
94
+
95
+ assertSerializable(source);
96
+ expect(source.endpoints.posts?.operations['posts.list']?.intent).toBe('read');
97
+ expect(source.endpoints.posts?.operations['posts.create']?.intent).toBe('create');
98
+ });
99
+
100
+ it('serializes an imported OpenAPI data source', () => {
101
+ const source: OpenApiDataSourceConfig = {
102
+ id: 'shop-openapi',
103
+ kind: 'openapi',
104
+ name: 'Shop OpenAPI',
105
+ baseUrl: 'https://shop.example.com/api',
106
+ import: {
107
+ url: 'https://shop.example.com/openapi.json',
108
+ version: '2026-05-19',
109
+ },
110
+ endpoints: {
111
+ products: {
112
+ id: 'products',
113
+ kind: 'http',
114
+ path: '/products/{productId}',
115
+ operations: {
116
+ getProduct: {
117
+ id: 'getProduct',
118
+ endpointId: 'products',
119
+ protocol: 'http',
120
+ intent: 'read',
121
+ method: 'GET',
122
+ path: '/products/{productId}',
123
+ request: {
124
+ parameters: [
125
+ {
126
+ name: 'productId',
127
+ location: 'path',
128
+ required: true,
129
+ schema: { type: 'string' },
130
+ },
131
+ ],
132
+ },
133
+ response: {
134
+ status: 200,
135
+ schema: {
136
+ type: 'object',
137
+ properties: {
138
+ id: { type: 'string' },
139
+ name: { type: 'string' },
140
+ price: { type: 'number' },
141
+ },
142
+ },
143
+ },
144
+ },
145
+ },
146
+ },
147
+ },
148
+ };
149
+
150
+ assertSerializable(source);
151
+ expect(source.import?.url).toContain('openapi.json');
152
+ expect(
153
+ source.endpoints.products?.operations.getProduct?.request?.parameters?.[0]?.location,
154
+ ).toBe('path');
155
+ });
156
+
157
+ it('serializes a GraphQL data source with query and mutation operations', () => {
158
+ const source: GraphQlDataSourceConfig = {
159
+ id: 'content-graphql',
160
+ kind: 'graphql',
161
+ endpointUrl: 'https://content.example.com/graphql',
162
+ introspection: {
163
+ enabled: true,
164
+ schemaVersion: '2026-05-19',
165
+ },
166
+ endpoints: {
167
+ graphql: {
168
+ id: 'graphql',
169
+ kind: 'graphql',
170
+ operations: {
171
+ PostsQuery: {
172
+ id: 'PostsQuery',
173
+ endpointId: 'graphql',
174
+ protocol: 'graphql',
175
+ intent: 'read',
176
+ request: {
177
+ schema: {
178
+ type: 'object',
179
+ properties: {
180
+ search: { type: 'string' },
181
+ },
182
+ },
183
+ },
184
+ response: {
185
+ schema: {
186
+ type: 'object',
187
+ properties: {
188
+ posts: {
189
+ type: 'array',
190
+ items: { type: 'object' },
191
+ },
192
+ },
193
+ },
194
+ },
195
+ },
196
+ CreatePost: {
197
+ id: 'CreatePost',
198
+ endpointId: 'graphql',
199
+ protocol: 'graphql',
200
+ intent: 'create',
201
+ request: {
202
+ schema: {
203
+ type: 'object',
204
+ required: ['title'],
205
+ properties: {
206
+ title: { type: 'string' },
207
+ },
208
+ },
209
+ },
210
+ response: {
211
+ schema: {
212
+ type: 'object',
213
+ properties: {
214
+ createPost: { type: 'object' },
215
+ },
216
+ },
217
+ },
218
+ },
219
+ },
220
+ },
221
+ },
222
+ };
223
+
224
+ assertSerializable(source);
225
+ expect(source.endpoints.graphql?.operations.PostsQuery?.protocol).toBe('graphql');
226
+ expect(source.endpoints.graphql?.operations.CreatePost?.intent).toBe('create');
227
+ });
228
+
229
+ it('serializes a managed API backed by a database adapter reference', () => {
230
+ const source: ManagedApiDataSourceConfig = {
231
+ id: 'app-managed-api',
232
+ kind: 'managed-api',
233
+ name: 'App managed API',
234
+ adapter: {
235
+ id: 'primary-db',
236
+ kind: 'database',
237
+ packageName: '@ankhorage/supabase-db',
238
+ },
239
+ resources: [
240
+ {
241
+ name: 'posts',
242
+ collection: {
243
+ name: 'posts',
244
+ schema: 'public',
245
+ primaryKey: 'id',
246
+ fields: [
247
+ { name: 'id', type: 'uuid', required: true, unique: true },
248
+ { name: 'title', type: 'text', required: true },
249
+ { name: 'published', type: 'boolean', defaultValue: false },
250
+ ],
251
+ },
252
+ operations: ['list', 'read', 'create', 'update', 'delete'],
253
+ },
254
+ ],
255
+ endpoints: {
256
+ posts: {
257
+ id: 'posts',
258
+ kind: 'database',
259
+ operations: {
260
+ 'posts.list': {
261
+ id: 'posts.list',
262
+ endpointId: 'posts',
263
+ protocol: 'database',
264
+ intent: 'read',
265
+ },
266
+ 'posts.delete': {
267
+ id: 'posts.delete',
268
+ endpointId: 'posts',
269
+ protocol: 'database',
270
+ intent: 'delete',
271
+ },
272
+ },
273
+ },
274
+ },
275
+ };
276
+
277
+ assertSerializable(source);
278
+ expect(source.adapter.kind).toBe('database');
279
+ expect(source.resources[0]?.collection.fields.map((field) => field.name)).toEqual([
280
+ 'id',
281
+ 'title',
282
+ 'published',
283
+ ]);
284
+ });
285
+
286
+ it('accepts a provider-neutral data source registry and diagnostics', () => {
287
+ const source: DataSourceConfig = {
288
+ id: 'public-rest',
289
+ kind: 'rest',
290
+ baseUrl: 'https://public.example.com',
291
+ endpoints: {},
292
+ };
293
+ const diagnostic: DataSourceDiagnostic = {
294
+ code: 'missing-operation',
295
+ severity: 'error',
296
+ message: 'Operation not found.',
297
+ dataSourceId: 'public-rest',
298
+ endpointId: 'posts',
299
+ operationId: 'posts.list',
300
+ };
301
+
302
+ assertSerializable({ source, diagnostic });
303
+ expect(diagnostic.code).toBe('missing-operation');
304
+ });
305
+ });
@@ -0,0 +1,41 @@
1
+ import type { DataSourceId, EndpointId, OperationId } from './ids';
2
+
3
+ export type DataDiagnosticSeverity = 'error' | 'info' | 'warning';
4
+
5
+ export type DataDiagnosticCode =
6
+ | 'ambiguous-server'
7
+ | 'duplicate-operation-id'
8
+ | 'invalid-config'
9
+ | 'missing-adapter'
10
+ | 'missing-credential'
11
+ | 'missing-data-source'
12
+ | 'missing-endpoint'
13
+ | 'missing-operation'
14
+ | 'missing-schema'
15
+ | 'network-error'
16
+ | 'parse-error'
17
+ | 'unsupported-auth-scheme'
18
+ | 'unsupported-schema'
19
+ | (string & {});
20
+
21
+ export interface DataSourceDiagnostic {
22
+ readonly code: DataDiagnosticCode;
23
+ readonly message: string;
24
+ readonly severity: DataDiagnosticSeverity;
25
+ readonly dataSourceId?: DataSourceId;
26
+ readonly endpointId?: EndpointId;
27
+ readonly operationId?: OperationId;
28
+ readonly path?: string;
29
+ readonly hint?: string;
30
+ }
31
+
32
+ export type DataSourceDiagnosticResult<TData> =
33
+ | {
34
+ readonly ok: true;
35
+ readonly data: TData;
36
+ readonly diagnostics?: readonly DataSourceDiagnostic[];
37
+ }
38
+ | {
39
+ readonly ok: false;
40
+ readonly diagnostics: readonly DataSourceDiagnostic[];
41
+ };
@@ -0,0 +1,20 @@
1
+ import type { EndpointId, OperationId } from './ids';
2
+ import type { DataOperationConfig } from './operations';
3
+ import type { CredentialRef } from './refs';
4
+ import type { DataContractValue } from './values';
5
+
6
+ export type DataEndpointKind = 'database' | 'graphql' | 'http' | (string & {});
7
+
8
+ export interface DataEndpointConfig {
9
+ readonly id: EndpointId;
10
+ readonly kind: DataEndpointKind;
11
+ readonly name?: string;
12
+ readonly description?: string;
13
+ readonly baseUrl?: string;
14
+ readonly path?: string;
15
+ readonly credential?: CredentialRef;
16
+ readonly operations: Readonly<Record<OperationId, DataOperationConfig>>;
17
+ readonly metadata?: DataContractValue;
18
+ }
19
+
20
+ export type DataEndpointRegistry = Readonly<Record<EndpointId, DataEndpointConfig>>;
@@ -0,0 +1,6 @@
1
+ export type DataSourceId = string;
2
+ export type EndpointId = string;
3
+ export type OperationId = string;
4
+ export type SchemaId = string;
5
+ export type CredentialId = string;
6
+ export type AdapterId = string;
@@ -0,0 +1,8 @@
1
+ export * from './diagnostics';
2
+ export * from './endpoints';
3
+ export * from './ids';
4
+ export * from './operations';
5
+ export * from './refs';
6
+ export * from './schemas';
7
+ export * from './sources';
8
+ export * from './values';
@@ -0,0 +1,66 @@
1
+ import type { EndpointId, OperationId } from './ids';
2
+ import type { CredentialRef } from './refs';
3
+ import type { DataSchemaSlot } from './schemas';
4
+ import type { DataContractValue } from './values';
5
+
6
+ export type DataOperationIntent = 'action' | 'create' | 'delete' | 'read' | 'update';
7
+
8
+ export type DataOperationMethod =
9
+ | 'DELETE'
10
+ | 'GET'
11
+ | 'HEAD'
12
+ | 'OPTIONS'
13
+ | 'PATCH'
14
+ | 'POST'
15
+ | 'PUT'
16
+ | (string & {});
17
+
18
+ export type DataOperationProtocol = 'database' | 'graphql' | 'http' | (string & {});
19
+
20
+ export type DataOperationParameterLocation = 'body' | 'cookie' | 'header' | 'path' | 'query';
21
+
22
+ export interface DataOperationParameter extends DataSchemaSlot {
23
+ readonly name: string;
24
+ readonly location: DataOperationParameterLocation;
25
+ readonly required?: boolean;
26
+ readonly description?: string;
27
+ readonly default?: DataContractValue;
28
+ }
29
+
30
+ export interface DataOperationRequest extends DataSchemaSlot {
31
+ readonly parameters?: readonly DataOperationParameter[];
32
+ readonly contentType?: string;
33
+ }
34
+
35
+ export interface DataOperationResponse extends DataSchemaSlot {
36
+ readonly status?: number | string;
37
+ readonly contentType?: string;
38
+ readonly description?: string;
39
+ }
40
+
41
+ export interface DataOperationPagination {
42
+ readonly kind: 'cursor' | 'limit-offset' | 'page' | 'unknown' | (string & {});
43
+ readonly cursorPath?: string;
44
+ readonly limitParameter?: string;
45
+ readonly offsetParameter?: string;
46
+ readonly pageParameter?: string;
47
+ readonly pageSizeParameter?: string;
48
+ }
49
+
50
+ export interface DataOperationConfig {
51
+ readonly id: OperationId;
52
+ readonly endpointId?: EndpointId;
53
+ readonly name?: string;
54
+ readonly description?: string;
55
+ readonly protocol: DataOperationProtocol;
56
+ readonly intent: DataOperationIntent;
57
+ readonly method?: DataOperationMethod;
58
+ readonly path?: string;
59
+ readonly request?: DataOperationRequest;
60
+ readonly response?: DataOperationResponse;
61
+ readonly pagination?: DataOperationPagination;
62
+ readonly credential?: CredentialRef;
63
+ readonly metadata?: DataContractValue;
64
+ }
65
+
66
+ export type DataOperationRegistry = Readonly<Record<OperationId, DataOperationConfig>>;
@@ -0,0 +1,21 @@
1
+ import type { AdapterId, CredentialId } from './ids';
2
+ import type { DataContractValue } from './values';
3
+
4
+ export type CredentialKind = 'apiKey' | 'basic' | 'bearer' | 'custom' | 'oauth2';
5
+
6
+ export interface CredentialRef {
7
+ readonly id: CredentialId;
8
+ readonly kind: CredentialKind | (string & {});
9
+ readonly label?: string;
10
+ readonly scope?: string;
11
+ }
12
+
13
+ export type AdapterKind = 'auth' | 'database' | 'storage' | 'transport' | (string & {});
14
+
15
+ export interface AdapterRef {
16
+ readonly id: AdapterId;
17
+ readonly kind: AdapterKind;
18
+ readonly packageName?: string;
19
+ readonly exportName?: string;
20
+ readonly config?: DataContractValue;
21
+ }
@@ -0,0 +1,46 @@
1
+ import type { SchemaId } from './ids';
2
+ import type { DataContractValue } from './values';
3
+
4
+ export type DataSchemaPrimitiveType =
5
+ | 'array'
6
+ | 'boolean'
7
+ | 'integer'
8
+ | 'null'
9
+ | 'number'
10
+ | 'object'
11
+ | 'string';
12
+
13
+ export interface DataSchemaRef {
14
+ readonly id: SchemaId;
15
+ }
16
+
17
+ export interface DataSchemaProperty {
18
+ readonly description?: string;
19
+ readonly schema: DataSchema;
20
+ }
21
+
22
+ export interface DataSchema {
23
+ readonly type?: DataSchemaPrimitiveType | readonly DataSchemaPrimitiveType[];
24
+ readonly title?: string;
25
+ readonly description?: string;
26
+ readonly enum?: readonly DataContractValue[];
27
+ readonly const?: DataContractValue;
28
+ readonly default?: DataContractValue;
29
+ readonly format?: string;
30
+ readonly nullable?: boolean;
31
+ readonly required?: readonly string[];
32
+ readonly properties?: Readonly<Record<string, DataSchema>>;
33
+ readonly additionalProperties?: boolean | DataSchema;
34
+ readonly items?: DataSchema;
35
+ readonly allOf?: readonly DataSchema[];
36
+ readonly anyOf?: readonly DataSchema[];
37
+ readonly oneOf?: readonly DataSchema[];
38
+ readonly ref?: DataSchemaRef;
39
+ }
40
+
41
+ export type DataSchemaRegistry = Readonly<Record<SchemaId, DataSchema>>;
42
+
43
+ export interface DataSchemaSlot {
44
+ readonly schema?: DataSchema;
45
+ readonly schemaRef?: DataSchemaRef;
46
+ }