@slates/proto 1.0.0-rc.2 → 1.0.0-rc.21

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 (57) hide show
  1. package/dist/index.cjs +1632 -2
  2. package/dist/index.d.cts +5397 -0
  3. package/dist/index.d.ts +5397 -4
  4. package/dist/index.module.js +1480 -2
  5. package/package.json +9 -8
  6. package/src/handler/provider.test.ts +121 -0
  7. package/src/handler/provider.ts +268 -27
  8. package/src/index.ts +1 -0
  9. package/src/messages/action.ts +51 -154
  10. package/src/messages/adapter.ts +72 -0
  11. package/src/messages/auth.ts +21 -13
  12. package/src/messages/config.ts +12 -3
  13. package/src/messages/controlFlow.ts +2 -1
  14. package/src/messages/hub.ts +92 -0
  15. package/src/messages/identify.ts +10 -3
  16. package/src/messages/index.ts +68 -14
  17. package/src/messages/legacyTrigger.ts +149 -0
  18. package/src/messages/tracing.ts +38 -0
  19. package/src/messages/triggerGroup.ts +383 -0
  20. package/src/messages/webhookErrors.ts +21 -0
  21. package/src/types/action.ts +25 -13
  22. package/src/types/adapter.ts +15 -0
  23. package/src/types/authMethod.ts +19 -1
  24. package/src/types/index.ts +2 -0
  25. package/src/types/triggerGroup.ts +49 -0
  26. package/src/version.ts +3 -0
  27. package/dist/handler/index.d.ts +0 -2
  28. package/dist/handler/index.d.ts.map +0 -1
  29. package/dist/handler/provider.d.ts +0 -46
  30. package/dist/handler/provider.d.ts.map +0 -1
  31. package/dist/index.cjs.map +0 -1
  32. package/dist/index.d.ts.map +0 -1
  33. package/dist/index.module.js.map +0 -1
  34. package/dist/index.umd.js +0 -2
  35. package/dist/index.umd.js.map +0 -1
  36. package/dist/messages/action.d.ts +0 -474
  37. package/dist/messages/action.d.ts.map +0 -1
  38. package/dist/messages/auth.d.ts +0 -496
  39. package/dist/messages/auth.d.ts.map +0 -1
  40. package/dist/messages/config.d.ts +0 -138
  41. package/dist/messages/config.d.ts.map +0 -1
  42. package/dist/messages/controlFlow.d.ts +0 -64
  43. package/dist/messages/controlFlow.d.ts.map +0 -1
  44. package/dist/messages/identify.d.ts +0 -48
  45. package/dist/messages/identify.d.ts.map +0 -1
  46. package/dist/messages/index.d.ts +0 -556
  47. package/dist/messages/index.d.ts.map +0 -1
  48. package/dist/messages/log.d.ts +0 -27
  49. package/dist/messages/log.d.ts.map +0 -1
  50. package/dist/types/action.d.ts +0 -98
  51. package/dist/types/action.d.ts.map +0 -1
  52. package/dist/types/authMethod.d.ts +0 -29
  53. package/dist/types/authMethod.d.ts.map +0 -1
  54. package/dist/types/index.d.ts +0 -4
  55. package/dist/types/index.d.ts.map +0 -1
  56. package/dist/types/participant.d.ts +0 -10
  57. package/dist/types/participant.d.ts.map +0 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@slates/proto",
3
- "version": "1.0.0-rc.2",
3
+ "version": "1.0.0-rc.21",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -11,7 +11,7 @@
11
11
  "package.json"
12
12
  ],
13
13
  "author": "Tobias Herber",
14
- "license": "Apache 2",
14
+ "license": "FSL 1.1",
15
15
  "type": "module",
16
16
  "source": "src/index.ts",
17
17
  "exports": {
@@ -23,23 +23,24 @@
23
23
  "main": "./dist/index.cjs",
24
24
  "module": "./dist/index.module.js",
25
25
  "types": "dist/index.d.ts",
26
- "unpkg": "./dist/index.umd.js",
26
+ "unpkg": "./dist/index.module.js",
27
27
  "scripts": {
28
28
  "test": "vitest run --passWithNoTests",
29
- "lint": "prettier src/**/*.ts --check",
30
- "build": "microbundle"
29
+ "build": "tsup --config ../../tsup.packages.config.ts",
30
+ "typecheck": "tsc --noEmit",
31
+ "biome:check": "biome check --write src"
31
32
  },
32
33
  "dependencies": {
33
34
  "@lowerdeck/emitter": "^1.0.4",
34
- "@lowerdeck/error": "^1.0.8",
35
+ "@lowerdeck/error": "^1.1.0",
35
36
  "@toon-format/toon": "^2.1.0",
36
37
  "axios": "^1.13.2",
37
38
  "zod": "^4.2.1"
38
39
  },
39
40
  "devDependencies": {
40
41
  "microbundle": "^0.15.1",
41
- "@slates/tsconfig": "^1.0.0",
42
+ "@slates/tsconfig": "1.0.0-rc.1",
42
43
  "typescript": "5.8.2",
43
44
  "vitest": "^3.1.2"
44
45
  }
45
- }
46
+ }
@@ -0,0 +1,121 @@
1
+ import { badRequestError, ServiceError } from '@lowerdeck/error';
2
+ import { describe, expect, it } from 'vitest';
3
+ import { SlateError } from '../../../provider/src/error/base';
4
+ import {
5
+ createSlatesProviderProtoHandler,
6
+ SlatesProviderProtoHandlerManager
7
+ } from './provider';
8
+
9
+ describe('SlatesProviderProtoHandlerManager', () => {
10
+ it('serializes SlateError instances directly into JSON-RPC errors', async () => {
11
+ let manager = await createSlatesProviderProtoHandler(async manager => {
12
+ manager.onRequest('slates/provider.identify', async () => {
13
+ throw new SlateError({
14
+ code: 'upstream.rate_limited',
15
+ kind: 'upstream',
16
+ message: 'GitHub rate limit exceeded',
17
+ retryable: true,
18
+ upstream: {
19
+ status: 429,
20
+ code: 'rate_limited'
21
+ },
22
+ baggage: {
23
+ scope: 'issues'
24
+ }
25
+ });
26
+ });
27
+ }).run();
28
+
29
+ let response = await SlatesProviderProtoHandlerManager.handleInput(manager, {
30
+ jsonrpc: '2.0',
31
+ id: 'req_1',
32
+ method: 'slates/provider.identify',
33
+ params: {}
34
+ });
35
+
36
+ expect(response).toEqual({
37
+ jsonrpc: '2.0',
38
+ id: 'req_1',
39
+ error: {
40
+ code: 'upstream.rate_limited',
41
+ kind: 'upstream',
42
+ message: 'GitHub rate limit exceeded',
43
+ retryable: true,
44
+ status: 429,
45
+ upstream: {
46
+ status: 429,
47
+ code: 'rate_limited'
48
+ },
49
+ baggage: {
50
+ scope: 'issues'
51
+ }
52
+ }
53
+ });
54
+ });
55
+
56
+ it('maps ServiceError instances to SlateError protocol responses', async () => {
57
+ let manager = await createSlatesProviderProtoHandler(async manager => {
58
+ manager.onRequest('slates/provider.identify', async () => {
59
+ throw new ServiceError(
60
+ badRequestError({
61
+ message: 'Invalid provider state'
62
+ })
63
+ );
64
+ });
65
+ }).run();
66
+
67
+ let response = await SlatesProviderProtoHandlerManager.handleInput(manager, {
68
+ jsonrpc: '2.0',
69
+ id: 'req_2',
70
+ method: 'slates/provider.identify',
71
+ params: {}
72
+ });
73
+
74
+ expect(response).toEqual({
75
+ jsonrpc: '2.0',
76
+ id: 'req_2',
77
+ error: {
78
+ code: 'request.bad',
79
+ kind: 'request',
80
+ message: 'Invalid provider state',
81
+ retryable: false,
82
+ status: 400,
83
+ baggage: {
84
+ serviceError: {
85
+ code: 'bad_request',
86
+ status: 400
87
+ }
88
+ }
89
+ }
90
+ });
91
+ });
92
+
93
+ it('returns slate errors for protocol validation failures', async () => {
94
+ let manager = await createSlatesProviderProtoHandler(async () => {}).run();
95
+
96
+ let response = await SlatesProviderProtoHandlerManager.handleInput(manager, {
97
+ jsonrpc: '1.0',
98
+ id: 'req_3',
99
+ method: 'slates/provider.identify',
100
+ params: {}
101
+ } as any);
102
+
103
+ expect(response).toEqual({
104
+ jsonrpc: '2.0',
105
+ id: 'req_3',
106
+ error: {
107
+ code: 'request.bad',
108
+ kind: 'request',
109
+ message: 'Invalid jsonrpc version',
110
+ retryable: false,
111
+ status: 400,
112
+ baggage: {
113
+ serviceError: {
114
+ code: 'bad_request',
115
+ status: 400
116
+ }
117
+ }
118
+ }
119
+ });
120
+ });
121
+ });
@@ -1,17 +1,16 @@
1
1
  import {
2
2
  badRequestError,
3
- internalServerError,
4
3
  isServiceError,
5
4
  notFoundError,
6
5
  validationError
7
6
  } from '@lowerdeck/error';
8
- import z from 'zod';
7
+ import type z from 'zod';
9
8
  import {
10
- SlatesNotifications,
9
+ type SlatesNotifications,
10
+ type SlatesRequests,
11
+ type SlatesResponsesByMethod,
11
12
  slatesNotificationsByMethod,
12
- SlatesRequests,
13
- slatesRequestsByMethod,
14
- SlatesResponsesByMethod
13
+ slatesRequestsByMethod
15
14
  } from '../messages';
16
15
 
17
16
  export class SlatesProviderProtoHandlerManager {
@@ -64,7 +63,7 @@ export class SlatesProviderProtoHandlerManager {
64
63
  return {
65
64
  jsonrpc: '2.0' as const,
66
65
  id: (input as any).id,
67
- error: badRequestError({ message: 'Invalid input' })
66
+ error: toSlateErrorResponse(badRequestError({ message: 'Invalid input' }))
68
67
  };
69
68
  }
70
69
 
@@ -72,7 +71,7 @@ export class SlatesProviderProtoHandlerManager {
72
71
  return {
73
72
  jsonrpc: '2.0' as const,
74
73
  id: (input as any).id,
75
- error: badRequestError({ message: 'Invalid jsonrpc version' })
74
+ error: toSlateErrorResponse(badRequestError({ message: 'Invalid jsonrpc version' }))
76
75
  };
77
76
  }
78
77
 
@@ -81,7 +80,9 @@ export class SlatesProviderProtoHandlerManager {
81
80
  return {
82
81
  jsonrpc: '2.0' as const,
83
82
  id: (input as any).id,
84
- error: badRequestError({ message: 'Invalid or missing method' })
83
+ error: toSlateErrorResponse(
84
+ badRequestError({ message: 'Invalid or missing method' })
85
+ )
85
86
  };
86
87
  }
87
88
 
@@ -90,7 +91,7 @@ export class SlatesProviderProtoHandlerManager {
90
91
  return {
91
92
  jsonrpc: '2.0' as const,
92
93
  id: (input as any).id,
93
- error: notFoundError('handler', input.method)
94
+ error: toSlateErrorResponse(notFoundError('handler', input.method))
94
95
  };
95
96
  }
96
97
 
@@ -99,14 +100,16 @@ export class SlatesProviderProtoHandlerManager {
99
100
  return {
100
101
  jsonrpc: '2.0' as const,
101
102
  id: (input as any).id,
102
- error: validationError({
103
- entity: 'request',
104
- message: 'Invalid request parameters',
105
- errors: parsed.error.issues.map(i => ({
106
- ...i,
107
- path: i.path.map(p => String(p))
108
- }))
109
- })
103
+ error: toSlateErrorResponse(
104
+ validationError({
105
+ entity: 'request',
106
+ message: 'Invalid request parameters',
107
+ errors: parsed.error.issues.map(i => ({
108
+ ...i,
109
+ path: i.path.map(p => String(p))
110
+ }))
111
+ })
112
+ )
110
113
  };
111
114
  }
112
115
 
@@ -123,20 +126,16 @@ export class SlatesProviderProtoHandlerManager {
123
126
  result
124
127
  };
125
128
  } catch (err) {
126
- if (isServiceError(err)) {
127
- return {
128
- jsonrpc: '2.0' as const,
129
- id: (input as any).id,
130
- error: err
131
- };
129
+ if (!getSlateErrorResponse(err) && !getServiceErrorData(err)) {
130
+ console.error(err);
132
131
  }
133
132
 
134
- console.error(err);
135
-
136
133
  return {
137
134
  jsonrpc: '2.0' as const,
138
135
  id: (input as any).id,
139
- error: internalServerError({ message: 'Internal server error' })
136
+ error: toSlateErrorResponse(err, {
137
+ message: 'Internal server error'
138
+ })
140
139
  };
141
140
  }
142
141
  }
@@ -160,3 +159,245 @@ export let createSlatesProviderProtoHandler = (
160
159
  return manager;
161
160
  }
162
161
  });
162
+
163
+ let toSlateErrorResponse = (
164
+ error: unknown,
165
+ defaults: Partial<{ code: string; message: string }> = {}
166
+ ) => {
167
+ let slateError = getSlateErrorResponse(error);
168
+ if (slateError) return slateError;
169
+
170
+ let serviceError = getServiceErrorData(error);
171
+ if (serviceError) {
172
+ return mapServiceErrorToSlateErrorResponse(serviceError, defaults);
173
+ }
174
+
175
+ return mapUnknownErrorToSlateErrorResponse(error, defaults);
176
+ };
177
+
178
+ let SERVICE_ERROR_CODE_MAP: Record<string, string> = {
179
+ invalid_data: 'input.invalid',
180
+ bad_request: 'request.bad',
181
+ not_found: 'resource.not_found',
182
+ unauthorized: 'auth.required',
183
+ forbidden: 'permission.denied',
184
+ invalid_version: 'request.invalid_version',
185
+ conflict: 'resource.conflict',
186
+ gone: 'resource.gone',
187
+ payment_required: 'payment.required',
188
+ precondition_failed: 'request.precondition_failed',
189
+ not_acceptable: 'request.not_acceptable',
190
+ not_implemented: 'operation.not_implemented',
191
+ too_many_requests: 'request.rate_limited',
192
+ timeout: 'internal.timeout',
193
+ internal_server_error: 'internal.unexpected'
194
+ };
195
+
196
+ let isRecord = (value: unknown): value is Record<string, unknown> =>
197
+ typeof value === 'object' && value !== null && !Array.isArray(value);
198
+
199
+ let isStructuredValue = (value: unknown): value is Record<string, unknown> | Function =>
200
+ (typeof value === 'object' || typeof value === 'function') && value !== null;
201
+
202
+ let isSlateErrorPayload = (
203
+ value: unknown
204
+ ): value is { code: string; message: string; kind?: string; [key: string]: unknown } =>
205
+ isRecord(value) &&
206
+ typeof value.code === 'string' &&
207
+ typeof value.message === 'string' &&
208
+ typeof value.kind === 'string';
209
+
210
+ let isServiceErrorData = (
211
+ value: unknown
212
+ ): value is {
213
+ status: number;
214
+ code: string;
215
+ message: string;
216
+ errors?: unknown;
217
+ hint?: string;
218
+ description?: string;
219
+ reason?: string;
220
+ [key: string]: unknown;
221
+ } =>
222
+ isRecord(value) &&
223
+ typeof value.status === 'number' &&
224
+ typeof value.code === 'string' &&
225
+ typeof value.message === 'string' &&
226
+ !('config' in value) &&
227
+ !('request' in value) &&
228
+ !('response' in value) &&
229
+ !('isAxiosError' in value);
230
+
231
+ let getSlateErrorResponse = (error: unknown) => {
232
+ if (
233
+ isStructuredValue(error) &&
234
+ 'name' in error &&
235
+ typeof error.name === 'string' &&
236
+ error.name.startsWith('SlateError') &&
237
+ 'toResponse' in error &&
238
+ typeof error.toResponse === 'function'
239
+ ) {
240
+ let response = error.toResponse();
241
+ return isSlateErrorPayload(response) ? response : null;
242
+ }
243
+
244
+ if (isSlateErrorPayload(error)) return error;
245
+
246
+ if (isStructuredValue(error) && 'data' in error && isSlateErrorPayload(error.data)) {
247
+ return error.data;
248
+ }
249
+
250
+ return null;
251
+ };
252
+
253
+ let getServiceErrorData = (error: unknown) => {
254
+ if (isServiceError(error) && isServiceErrorData(error.data)) {
255
+ return error.data;
256
+ }
257
+
258
+ if (isServiceErrorData(error)) return error;
259
+
260
+ if (isStructuredValue(error) && 'data' in error && isServiceErrorData(error.data)) {
261
+ return error.data;
262
+ }
263
+
264
+ if (
265
+ isStructuredValue(error) &&
266
+ 'toResponse' in error &&
267
+ typeof error.toResponse === 'function'
268
+ ) {
269
+ let response = error.toResponse();
270
+ if (isServiceErrorData(response)) return response;
271
+ }
272
+
273
+ return null;
274
+ };
275
+
276
+ let mapKind = (code: string) => {
277
+ if (code.startsWith('declaration.')) return 'declaration';
278
+ if (code.startsWith('input.')) return 'validation';
279
+ if (code.startsWith('request.')) return 'request';
280
+ if (code.startsWith('config.')) return 'config';
281
+ if (code.startsWith('auth.') || code.startsWith('permission.')) return 'auth';
282
+ if (code.startsWith('resource.')) return 'resource';
283
+ if (code.startsWith('payment.')) return 'payment';
284
+ if (code.startsWith('transport.')) return 'transport';
285
+ if (code.startsWith('upstream.')) return 'upstream';
286
+ return 'internal';
287
+ };
288
+
289
+ let mapRetryable = (code: string) =>
290
+ code === 'request.rate_limited' ||
291
+ code === 'upstream.rate_limited' ||
292
+ code === 'upstream.timeout' ||
293
+ code === 'upstream.network_error' ||
294
+ code === 'upstream.unavailable' ||
295
+ code === 'upstream.error' ||
296
+ code === 'transport.invoke_failed' ||
297
+ code === 'internal.timeout';
298
+
299
+ let mapServiceErrorCode = (code: string, status?: number) => {
300
+ if (SERVICE_ERROR_CODE_MAP[code]) return SERVICE_ERROR_CODE_MAP[code];
301
+ if (status === 400) return 'request.bad';
302
+ if (status === 401) return 'auth.required';
303
+ if (status === 402) return 'payment.required';
304
+ if (status === 403) return 'permission.denied';
305
+ if (status === 404) return 'resource.not_found';
306
+ if (status === 406) return 'request.not_acceptable';
307
+ if (status === 409) return 'resource.conflict';
308
+ if (status === 410) return 'resource.gone';
309
+ if (status === 412) return 'request.precondition_failed';
310
+ if (status === 429) return 'request.rate_limited';
311
+ if (status === 501) return 'operation.not_implemented';
312
+ if (status === 504) return 'internal.timeout';
313
+ if (status && status >= 500) return 'internal.unexpected';
314
+ return 'internal.unexpected';
315
+ };
316
+
317
+ let mapIssues = (errors: unknown) => {
318
+ if (!Array.isArray(errors)) return undefined;
319
+ let issues = errors.map(error => {
320
+ if (!isRecord(error)) {
321
+ return { message: String(error) };
322
+ }
323
+
324
+ return {
325
+ ...error,
326
+ path: Array.isArray(error.path) ? error.path.map(entry => String(entry)) : undefined,
327
+ code: typeof error.code === 'string' ? error.code : undefined,
328
+ message: typeof error.message === 'string' ? error.message : 'Validation issue'
329
+ };
330
+ });
331
+ return issues.length > 0 ? issues : undefined;
332
+ };
333
+
334
+ let mapServiceErrorToSlateErrorResponse = (
335
+ error: {
336
+ status: number;
337
+ code: string;
338
+ message: string;
339
+ errors?: unknown;
340
+ hint?: string;
341
+ description?: string;
342
+ reason?: string;
343
+ [key: string]: unknown;
344
+ },
345
+ defaults: Partial<{ code: string; message: string }> = {}
346
+ ) => {
347
+ let code = defaults.code ?? mapServiceErrorCode(error.code, error.status);
348
+ let extraEntries = Object.entries(error).filter(
349
+ ([key]) =>
350
+ ![
351
+ 'status',
352
+ 'code',
353
+ 'message',
354
+ 'errors',
355
+ 'hint',
356
+ 'description',
357
+ 'reason',
358
+ 'object',
359
+ 'ok'
360
+ ].includes(key)
361
+ );
362
+
363
+ return {
364
+ code,
365
+ kind: mapKind(code),
366
+ message: error.message,
367
+ retryable: mapRetryable(code),
368
+ status: error.status,
369
+ ...(mapIssues(error.errors) ? { issues: mapIssues(error.errors) } : {}),
370
+ baggage: {
371
+ serviceError: {
372
+ code: error.code,
373
+ status: error.status,
374
+ hint: error.hint,
375
+ description: error.description,
376
+ reason: error.reason
377
+ },
378
+ ...(extraEntries.length > 0
379
+ ? { serviceErrorData: Object.fromEntries(extraEntries) }
380
+ : {})
381
+ }
382
+ };
383
+ };
384
+
385
+ let mapUnknownErrorToSlateErrorResponse = (
386
+ error: unknown,
387
+ defaults: Partial<{ code: string; message: string }> = {}
388
+ ) => ({
389
+ code: defaults.code ?? 'internal.unexpected',
390
+ kind: 'internal',
391
+ message:
392
+ defaults.message ??
393
+ (error instanceof Error ? error.message : 'An unexpected internal error occurred.'),
394
+ retryable: false,
395
+ status: 500,
396
+ ...(error instanceof Error
397
+ ? {
398
+ baggage: {
399
+ originalName: error.name
400
+ }
401
+ }
402
+ : {})
403
+ });
package/src/index.ts CHANGED
@@ -1,3 +1,4 @@
1
1
  export * from './handler';
2
2
  export * from './messages';
3
3
  export * from './types';
4
+ export * from './version';