@slates/provider 1.0.0-rc.7 → 1.0.0-rc.9

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 (89) hide show
  1. package/dist/index.cjs +1619 -2
  2. package/dist/index.d.cts +620 -0
  3. package/dist/index.d.ts +620 -9
  4. package/dist/index.module.js +1547 -2
  5. package/package.json +6 -4
  6. package/src/action/action.ts +18 -3
  7. package/src/action/attachment.ts +41 -0
  8. package/src/action/builder.ts +21 -4
  9. package/src/action/index.ts +2 -2
  10. package/src/action/scopes.test.ts +77 -0
  11. package/src/action/scopes.ts +101 -0
  12. package/src/action/tool.ts +8 -4
  13. package/src/action/trigger.ts +11 -11
  14. package/src/auth/auth.ts +3 -3
  15. package/src/auth/types.ts +7 -6
  16. package/src/axios/index.ts +55 -23
  17. package/src/axios/trace.ts +443 -0
  18. package/src/config/config.ts +3 -3
  19. package/src/context/context.ts +29 -2
  20. package/src/context/hook.ts +1 -1
  21. package/src/error/axios.ts +219 -0
  22. package/src/error/base.test.ts +178 -0
  23. package/src/error/base.ts +108 -2
  24. package/src/error/declaration.ts +7 -2
  25. package/src/error/defaults.ts +50 -0
  26. package/src/error/index.ts +3 -0
  27. package/src/error/service.ts +160 -0
  28. package/src/error/types.ts +73 -0
  29. package/src/error/utils.ts +155 -0
  30. package/src/index.ts +1 -0
  31. package/src/logger/logger.test.ts +57 -0
  32. package/src/logger/logger.ts +72 -11
  33. package/src/specification/slate.ts +2 -2
  34. package/src/specification/specification.ts +2 -2
  35. package/dist/action/action.d.ts +0 -98
  36. package/dist/action/action.d.ts.map +0 -1
  37. package/dist/action/builder.d.ts +0 -27
  38. package/dist/action/builder.d.ts.map +0 -1
  39. package/dist/action/index.d.ts +0 -5
  40. package/dist/action/index.d.ts.map +0 -1
  41. package/dist/action/tool.d.ts +0 -14
  42. package/dist/action/tool.d.ts.map +0 -1
  43. package/dist/action/trigger.d.ts +0 -32
  44. package/dist/action/trigger.d.ts.map +0 -1
  45. package/dist/auth/auth.d.ts +0 -30
  46. package/dist/auth/auth.d.ts.map +0 -1
  47. package/dist/auth/index.d.ts +0 -3
  48. package/dist/auth/index.d.ts.map +0 -1
  49. package/dist/auth/types.d.ts +0 -150
  50. package/dist/auth/types.d.ts.map +0 -1
  51. package/dist/axios/index.d.ts +0 -4
  52. package/dist/axios/index.d.ts.map +0 -1
  53. package/dist/config/config.d.ts +0 -26
  54. package/dist/config/config.d.ts.map +0 -1
  55. package/dist/config/index.d.ts +0 -2
  56. package/dist/config/index.d.ts.map +0 -1
  57. package/dist/context/context.d.ts +0 -20
  58. package/dist/context/context.d.ts.map +0 -1
  59. package/dist/context/hook.d.ts +0 -4
  60. package/dist/context/hook.d.ts.map +0 -1
  61. package/dist/context/index.d.ts +0 -3
  62. package/dist/context/index.d.ts.map +0 -1
  63. package/dist/error/base.d.ts +0 -5
  64. package/dist/error/base.d.ts.map +0 -1
  65. package/dist/error/declaration.d.ts +0 -6
  66. package/dist/error/declaration.d.ts.map +0 -1
  67. package/dist/error/index.d.ts +0 -3
  68. package/dist/error/index.d.ts.map +0 -1
  69. package/dist/index.cjs.map +0 -1
  70. package/dist/index.d.ts.map +0 -1
  71. package/dist/index.modern.js +0 -2
  72. package/dist/index.modern.js.map +0 -1
  73. package/dist/index.module.js.map +0 -1
  74. package/dist/index.umd.js +0 -2
  75. package/dist/index.umd.js.map +0 -1
  76. package/dist/logger/index.d.ts +0 -2
  77. package/dist/logger/index.d.ts.map +0 -1
  78. package/dist/logger/logger.d.ts +0 -20
  79. package/dist/logger/logger.d.ts.map +0 -1
  80. package/dist/specification/index.d.ts +0 -3
  81. package/dist/specification/index.d.ts.map +0 -1
  82. package/dist/specification/slate.d.ts +0 -15
  83. package/dist/specification/slate.d.ts.map +0 -1
  84. package/dist/specification/specification.d.ts +0 -30
  85. package/dist/specification/specification.d.ts.map +0 -1
  86. package/dist/specification/zero.d.ts +0 -13
  87. package/dist/specification/zero.d.ts.map +0 -1
  88. package/dist/tokens.d.ts +0 -34
  89. package/dist/tokens.d.ts.map +0 -1
@@ -0,0 +1,160 @@
1
+ import { isServiceError } from '@lowerdeck/error';
2
+ import type { ErrorData, ErrorRecord, ServiceError } from '@lowerdeck/error';
3
+ import { DEFAULT_CODE, SERVICE_ERROR_CODE_MAP } from './defaults';
4
+ import type { SlateErrorInput, SlateErrorIssue, SlateServiceErrorData } from './types';
5
+ import { cleanRecord, isRecord, normalizeKind, normalizeRetryable, sanitizeForBaggage } from './utils';
6
+
7
+ let SERVICE_ERROR_DATA_RESERVED_KEYS = new Set([
8
+ 'status',
9
+ 'code',
10
+ 'message',
11
+ 'hint',
12
+ 'description',
13
+ 'reason',
14
+ 'errors',
15
+ 'object',
16
+ 'ok'
17
+ ]);
18
+
19
+ let isStructuredValue = (value: unknown): value is Record<string, unknown> | Function =>
20
+ (typeof value === 'object' || typeof value === 'function') && value !== null;
21
+
22
+ let mapServiceErrorCodeByStatus = (status?: number) => {
23
+ if (status === 400) return 'request.bad';
24
+ if (status === 401) return 'auth.required';
25
+ if (status === 402) return 'payment.required';
26
+ if (status === 403) return 'permission.denied';
27
+ if (status === 404) return 'resource.not_found';
28
+ if (status === 406) return 'request.not_acceptable';
29
+ if (status === 409) return 'resource.conflict';
30
+ if (status === 410) return 'resource.gone';
31
+ if (status === 412) return 'request.precondition_failed';
32
+ if (status === 429) return 'request.rate_limited';
33
+ if (status === 501) return 'operation.not_implemented';
34
+ if (status === 504) return 'internal.timeout';
35
+ if (status && status >= 500) return 'internal.unexpected';
36
+ return DEFAULT_CODE;
37
+ };
38
+
39
+ let normalizeServiceErrorIssues = (errors: unknown): SlateErrorIssue[] | undefined => {
40
+ if (!Array.isArray(errors)) return undefined;
41
+
42
+ let issues = errors.map(error => {
43
+ if (!isRecord(error)) {
44
+ return {
45
+ message: String(error)
46
+ };
47
+ }
48
+
49
+ let path = Array.isArray(error.path)
50
+ ? error.path.map(entry => String(entry))
51
+ : undefined;
52
+
53
+ return {
54
+ ...error,
55
+ path,
56
+ code: typeof error.code === 'string' ? error.code : undefined,
57
+ message:
58
+ typeof error.message === 'string'
59
+ ? error.message
60
+ : typeof error.hint === 'string'
61
+ ? error.hint
62
+ : 'Validation issue'
63
+ };
64
+ });
65
+
66
+ return issues.length > 0 ? issues : undefined;
67
+ };
68
+
69
+ let getServiceErrorBaggage = (data: SlateServiceErrorData) => {
70
+ let extraEntries = Object.entries(data).filter(([key]) => !SERVICE_ERROR_DATA_RESERVED_KEYS.has(key));
71
+
72
+ return cleanRecord({
73
+ serviceError: {
74
+ code: data.code,
75
+ status: data.status,
76
+ hint: data.hint,
77
+ description: data.description,
78
+ reason: data.reason
79
+ },
80
+ ...(extraEntries.length > 0
81
+ ? {
82
+ serviceErrorData: sanitizeForBaggage(Object.fromEntries(extraEntries))
83
+ }
84
+ : {})
85
+ });
86
+ };
87
+
88
+ export let mapServiceErrorCode = (code: string, status?: number) =>
89
+ SERVICE_ERROR_CODE_MAP[code] ?? mapServiceErrorCodeByStatus(status);
90
+
91
+ export let isServiceErrorData = (value: unknown): value is SlateServiceErrorData =>
92
+ isRecord(value) &&
93
+ typeof value.status === 'number' &&
94
+ typeof value.code === 'string' &&
95
+ typeof value.message === 'string' &&
96
+ !('config' in value) &&
97
+ !('request' in value) &&
98
+ !('response' in value) &&
99
+ !('isAxiosError' in value);
100
+
101
+ export let getServiceErrorData = (error: unknown): SlateServiceErrorData | null => {
102
+ if (isServiceError(error)) {
103
+ return isServiceErrorData(error.data) ? error.data : null;
104
+ }
105
+
106
+ if (isServiceErrorData(error)) {
107
+ return error;
108
+ }
109
+
110
+ if (isStructuredValue(error) && 'data' in error && isServiceErrorData(error.data)) {
111
+ return error.data;
112
+ }
113
+
114
+ if (isStructuredValue(error) && 'toResponse' in error && typeof error.toResponse === 'function') {
115
+ let response = error.toResponse();
116
+ return isServiceErrorData(response) ? response : null;
117
+ }
118
+
119
+ return null;
120
+ };
121
+
122
+ export let mapServiceErrorToSlateErrorInput = (
123
+ error:
124
+ | ServiceError<ErrorRecord<any, any>>
125
+ | ErrorRecord<any, any>
126
+ | ErrorData<any, any>
127
+ | SlateServiceErrorData,
128
+ defaults: Partial<SlateErrorInput> = {}
129
+ ): SlateErrorInput => {
130
+ let data = getServiceErrorData(error);
131
+ if (!data) {
132
+ return {
133
+ code: defaults.code ?? DEFAULT_CODE,
134
+ message: defaults.message ?? 'An unexpected internal error occurred.',
135
+ kind: defaults.kind,
136
+ retryable: defaults.retryable,
137
+ status: defaults.status,
138
+ provider: defaults.provider,
139
+ upstream: defaults.upstream,
140
+ baggage: defaults.baggage
141
+ };
142
+ }
143
+
144
+ let code = defaults.code ?? mapServiceErrorCode(data.code, data.status);
145
+
146
+ return {
147
+ code,
148
+ message: defaults.message ?? data.message,
149
+ kind: defaults.kind ?? normalizeKind(code),
150
+ retryable: defaults.retryable ?? normalizeRetryable(code),
151
+ status: defaults.status ?? data.status,
152
+ issues: defaults.issues ?? normalizeServiceErrorIssues(data.errors),
153
+ provider: defaults.provider,
154
+ upstream: defaults.upstream,
155
+ baggage: cleanRecord({
156
+ ...(defaults.baggage ?? {}),
157
+ ...getServiceErrorBaggage(data)
158
+ })
159
+ };
160
+ };
@@ -0,0 +1,73 @@
1
+ import type { AxiosError, AxiosResponse } from 'axios';
2
+ import type { SlateError } from './base';
3
+
4
+ export type SlateErrorKind =
5
+ | 'declaration'
6
+ | 'validation'
7
+ | 'request'
8
+ | 'auth'
9
+ | 'config'
10
+ | 'resource'
11
+ | 'payment'
12
+ | 'upstream'
13
+ | 'transport'
14
+ | 'internal';
15
+
16
+ export interface SlateErrorIssue {
17
+ path?: string[];
18
+ code?: string;
19
+ message: string;
20
+ [key: string]: unknown;
21
+ }
22
+
23
+ export interface SlateErrorProviderInfo {
24
+ key?: string;
25
+ service?: string;
26
+ operation?: string;
27
+ }
28
+
29
+ export interface SlateErrorUpstreamInfo {
30
+ status?: number;
31
+ code?: string;
32
+ type?: string;
33
+ requestId?: string;
34
+ method?: string;
35
+ url?: string;
36
+ }
37
+
38
+ export interface SlateErrorResponse {
39
+ code: string;
40
+ message: string;
41
+ kind: SlateErrorKind;
42
+ retryable?: boolean;
43
+ status?: number;
44
+ issues?: SlateErrorIssue[];
45
+ provider?: SlateErrorProviderInfo;
46
+ upstream?: SlateErrorUpstreamInfo;
47
+ baggage?: Record<string, unknown>;
48
+ }
49
+
50
+ export interface SlateErrorInput extends Omit<SlateErrorResponse, 'kind'> {
51
+ kind?: SlateErrorKind;
52
+ cause?: unknown;
53
+ }
54
+
55
+ export interface SlateAxiosErrorOptions {
56
+ defaults?: Partial<SlateErrorResponse>;
57
+ extractResponseData?: (response: AxiosResponse) => unknown;
58
+ mapAxiosError?: (
59
+ error: AxiosError,
60
+ inferred: SlateErrorResponse
61
+ ) => SlateError | Partial<SlateErrorResponse> | void;
62
+ }
63
+
64
+ export interface SlateServiceErrorData {
65
+ status: number;
66
+ code: string;
67
+ message: string;
68
+ hint?: string;
69
+ description?: string;
70
+ reason?: string;
71
+ errors?: unknown;
72
+ [key: string]: unknown;
73
+ }
@@ -0,0 +1,155 @@
1
+ import { DEFAULT_CODE, DEFAULT_MESSAGES } from './defaults';
2
+ import type {
3
+ SlateErrorInput,
4
+ SlateErrorKind,
5
+ SlateErrorProviderInfo,
6
+ SlateErrorResponse,
7
+ SlateErrorUpstreamInfo
8
+ } from './types';
9
+
10
+ export let isRecord = (value: unknown): value is Record<string, unknown> =>
11
+ typeof value === 'object' && value !== null && !Array.isArray(value);
12
+
13
+ export let normalizeKind = (code: string, kind?: SlateErrorKind): SlateErrorKind => {
14
+ if (kind) return kind;
15
+ if (code.startsWith('declaration.')) return 'declaration';
16
+ if (code.startsWith('input.')) return 'validation';
17
+ if (code.startsWith('request.')) return 'request';
18
+ if (code.startsWith('config.')) return 'config';
19
+ if (code.startsWith('auth.') || code.startsWith('permission.')) return 'auth';
20
+ if (code.startsWith('resource.')) return 'resource';
21
+ if (code.startsWith('payment.')) return 'payment';
22
+ if (code.startsWith('transport.')) return 'transport';
23
+ if (code.startsWith('upstream.')) return 'upstream';
24
+ return 'internal';
25
+ };
26
+
27
+ export let normalizeStatus = (code: string, status?: number) => {
28
+ if (status) return status;
29
+ if (code === 'input.invalid' || code === 'request.bad' || code === 'config.invalid')
30
+ return 400;
31
+ if (code === 'request.invalid_version') return 400;
32
+ if (code === 'auth.required' || code === 'auth.invalid' || code === 'auth.expired')
33
+ return 401;
34
+ if (code === 'payment.required') return 402;
35
+ if (code === 'permission.denied') return 403;
36
+ if (code === 'resource.not_found') return 404;
37
+ if (code === 'resource.conflict') return 409;
38
+ if (code === 'resource.gone') return 410;
39
+ if (code === 'request.not_acceptable') return 406;
40
+ if (code === 'request.precondition_failed') return 412;
41
+ if (code === 'request.rate_limited' || code === 'upstream.rate_limited') return 429;
42
+ if (code === 'operation.not_implemented') return 501;
43
+ if (code === 'upstream.invalid_request') return 400;
44
+ if (code === 'upstream.timeout' || code === 'internal.timeout') return 504;
45
+ if (code === 'upstream.unavailable') return 503;
46
+ if (code === 'upstream.error') return 502;
47
+ return undefined;
48
+ };
49
+
50
+ export let normalizeRetryable = (code: string, retryable?: boolean) => {
51
+ if (typeof retryable === 'boolean') return retryable;
52
+ return (
53
+ code === 'request.rate_limited' ||
54
+ code === 'upstream.rate_limited' ||
55
+ code === 'upstream.timeout' ||
56
+ code === 'upstream.network_error' ||
57
+ code === 'upstream.unavailable' ||
58
+ code === 'upstream.error' ||
59
+ code === 'transport.invoke_failed' ||
60
+ code === 'internal.timeout'
61
+ );
62
+ };
63
+
64
+ export let getDefaultMessage = (code: string, message?: string) => {
65
+ if (message && message.trim()) return message;
66
+ return DEFAULT_MESSAGES[code] ?? DEFAULT_MESSAGES[DEFAULT_CODE];
67
+ };
68
+
69
+ export let cleanRecord = <Value extends Record<string, unknown> | undefined>(value: Value) => {
70
+ if (!value) return undefined;
71
+
72
+ let entries = Object.entries(value).filter(([, entry]) => entry !== undefined);
73
+ if (entries.length === 0) return undefined;
74
+ return Object.fromEntries(entries) as Value;
75
+ };
76
+
77
+ export let mergeRecords = (
78
+ left?: Record<string, unknown>,
79
+ right?: Record<string, unknown>
80
+ ): Record<string, unknown> | undefined => {
81
+ if (!left && !right) return undefined;
82
+ return cleanRecord({
83
+ ...(left ?? {}),
84
+ ...(right ?? {})
85
+ });
86
+ };
87
+
88
+ export let mergeSlateErrorData = (
89
+ base: SlateErrorResponse,
90
+ override?: Partial<SlateErrorResponse>
91
+ ): SlateErrorResponse => {
92
+ if (!override) return base;
93
+
94
+ return {
95
+ ...base,
96
+ ...override,
97
+ provider: cleanRecord({
98
+ ...(base.provider ?? {}),
99
+ ...(override.provider ?? {})
100
+ }) as SlateErrorProviderInfo | undefined,
101
+ upstream: cleanRecord({
102
+ ...(base.upstream ?? {}),
103
+ ...(override.upstream ?? {})
104
+ }) as SlateErrorUpstreamInfo | undefined,
105
+ baggage: mergeRecords(base.baggage, override.baggage),
106
+ issues: override.issues ?? base.issues
107
+ };
108
+ };
109
+
110
+ export let sanitizeForBaggage = (value: unknown, depth = 0): unknown => {
111
+ if (value == null) return value;
112
+ if (depth >= 4) return '[truncated]';
113
+ if (typeof value === 'string') {
114
+ return value.length > 2_000 ? `${value.slice(0, 2_000)}...[truncated]` : value;
115
+ }
116
+ if (typeof value === 'number' || typeof value === 'boolean' || typeof value === 'bigint') {
117
+ return value;
118
+ }
119
+ if (Array.isArray(value)) {
120
+ return value.slice(0, 20).map(item => sanitizeForBaggage(item, depth + 1));
121
+ }
122
+ if (isRecord(value)) {
123
+ return Object.fromEntries(
124
+ Object.entries(value)
125
+ .slice(0, 30)
126
+ .map(([key, entry]) => [key, sanitizeForBaggage(entry, depth + 1)])
127
+ );
128
+ }
129
+ return String(value);
130
+ };
131
+
132
+ export let normalizeSlateErrorInput = (input: SlateErrorInput): SlateErrorResponse => {
133
+ let code = input.code || DEFAULT_CODE;
134
+ return {
135
+ code,
136
+ message: getDefaultMessage(code, input.message),
137
+ kind: normalizeKind(code, input.kind),
138
+ retryable: normalizeRetryable(code, input.retryable),
139
+ status: normalizeStatus(code, input.status ?? input.upstream?.status),
140
+ issues: input.issues,
141
+ provider: cleanRecord(input.provider as Record<string, unknown> | undefined) as
142
+ | SlateErrorProviderInfo
143
+ | undefined,
144
+ upstream: cleanRecord({
145
+ ...input.upstream,
146
+ status: input.upstream?.status ?? input.status
147
+ }) as SlateErrorUpstreamInfo | undefined,
148
+ baggage: cleanRecord(input.baggage)
149
+ };
150
+ };
151
+
152
+ export let isSlateErrorResponse = (error: unknown): error is SlateErrorResponse => {
153
+ if (!isRecord(error)) return false;
154
+ return typeof error.code === 'string' && typeof error.message === 'string';
155
+ };
package/src/index.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  export * from './action';
2
+ export * from './action/attachment';
2
3
  export * from './auth';
3
4
  export * from './axios';
4
5
  export * from './config';
@@ -0,0 +1,57 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { SlateLogger, type SlateLogEntry } from './logger';
3
+
4
+ let flushLogs = async () => {
5
+ await new Promise(resolve => setTimeout(resolve, 20));
6
+ };
7
+
8
+ describe('SlateLogger', () => {
9
+ it('keeps a readable message and structured data for object logs', async () => {
10
+ let entries: SlateLogEntry[] = [];
11
+ let logger = new SlateLogger([
12
+ batch => {
13
+ entries.push(...batch);
14
+ }
15
+ ]);
16
+
17
+ logger.info({
18
+ message: 'Invoking tool action',
19
+ providerId: 'demo-slate',
20
+ actionId: 'echo',
21
+ phase: 'start'
22
+ });
23
+
24
+ await flushLogs();
25
+
26
+ expect(entries[0]).toMatchObject({
27
+ type: 'info',
28
+ message: 'Invoking tool action',
29
+ data: {
30
+ providerId: 'demo-slate',
31
+ actionId: 'echo',
32
+ phase: 'start'
33
+ }
34
+ });
35
+ });
36
+
37
+ it('captures error details as structured metadata', async () => {
38
+ let entries: SlateLogEntry[] = [];
39
+ let logger = new SlateLogger([
40
+ batch => {
41
+ entries.push(...batch);
42
+ }
43
+ ]);
44
+
45
+ logger.error(new Error('boom'));
46
+
47
+ await flushLogs();
48
+
49
+ expect(entries[0]?.message).toBe('Error: boom');
50
+ expect(entries[0]?.data).toMatchObject({
51
+ error: {
52
+ name: 'Error',
53
+ message: 'boom'
54
+ }
55
+ });
56
+ });
57
+ });
@@ -1,6 +1,7 @@
1
1
  import { encode } from '@toon-format/toon';
2
2
 
3
3
  export type SlateLogType = 'info' | 'warn' | 'error' | 'progress';
4
+ export type SlateLogFields = Record<string, unknown>;
4
5
 
5
6
  export type SlateLogMessageInput =
6
7
  | string
@@ -12,22 +13,79 @@ export type SlateLogMessageInput =
12
13
  | object
13
14
  | SlateLogMessageInput[];
14
15
 
15
- let formatMessage = (message: SlateLogMessageInput): string => {
16
+ let isRecord = (value: unknown): value is Record<string, unknown> =>
17
+ typeof value === 'object' && value !== null && !Array.isArray(value);
18
+
19
+ let normalizeMessage = (
20
+ message: SlateLogMessageInput
21
+ ): {
22
+ message: string;
23
+ data?: SlateLogFields;
24
+ } => {
25
+ if (Array.isArray(message)) {
26
+ let parts = message.map(part => normalizeMessage(part));
27
+ let combinedMessage = parts
28
+ .map(part => part.message)
29
+ .filter(Boolean)
30
+ .join(' ')
31
+ .trim();
32
+
33
+ let dataParts = parts
34
+ .map(part => {
35
+ if (part.data) {
36
+ return {
37
+ message: part.message,
38
+ ...part.data
39
+ };
40
+ }
41
+
42
+ return part.message;
43
+ })
44
+ .filter(Boolean);
45
+
46
+ return {
47
+ message: combinedMessage || 'Log entry',
48
+ data: dataParts.length > 0 ? { parts: dataParts } : undefined
49
+ };
50
+ }
51
+
16
52
  if (typeof message === 'object') {
17
- if (message === null) return 'null';
53
+ if (message === null) return { message: 'null' };
18
54
  if (message instanceof Error) {
19
- return `[ERROR]: ${message.name}: ${message.message}\n${message.stack}`;
55
+ return {
56
+ message: `${message.name}: ${message.message}`,
57
+ data: {
58
+ error: {
59
+ name: message.name,
60
+ message: message.message,
61
+ stack: message.stack
62
+ }
63
+ }
64
+ };
20
65
  }
21
- return encode(message);
66
+
67
+ if (isRecord(message) && typeof message.message === 'string') {
68
+ let { message: text, ...data } = message;
69
+ return {
70
+ message: text || 'Log entry',
71
+ data: Object.keys(data).length > 0 ? data : undefined
72
+ };
73
+ }
74
+
75
+ return {
76
+ message: encode(message),
77
+ data: isRecord(message) ? message : undefined
78
+ };
22
79
  }
23
80
 
24
- return String(message);
81
+ return { message: String(message) };
25
82
  };
26
83
 
27
84
  export interface SlateLogEntry {
28
85
  type: SlateLogType;
29
86
  message: string;
30
87
  timestamp: Date;
88
+ data?: SlateLogFields;
31
89
  }
32
90
 
33
91
  export type SlateLogListener = (entries: SlateLogEntry[]) => void;
@@ -44,9 +102,10 @@ export class SlateLogger {
44
102
  private flush() {
45
103
  if (this.#timer) clearTimeout(this.#timer);
46
104
  if (this.#logs.length === 0) return;
105
+ let logs = this.#logs.splice(0);
47
106
 
48
107
  for (let listener of this.#listeners) {
49
- listener(this.#logs);
108
+ listener(logs);
50
109
  }
51
110
  }
52
111
 
@@ -59,27 +118,29 @@ export class SlateLogger {
59
118
  }
60
119
 
61
120
  log(type: SlateLogType, message: SlateLogMessageInput) {
121
+ let normalized = normalizeMessage(message);
62
122
  this.#logs.push({
63
123
  type,
64
124
  timestamp: new Date(),
65
- message: formatMessage(message)
125
+ message: normalized.message,
126
+ data: normalized.data
66
127
  });
67
128
  this.scheduleFlush();
68
129
  }
69
130
 
70
131
  info(...message: SlateLogMessageInput[]) {
71
- this.log('info', message.flat());
132
+ this.log('info', message.length === 1 ? message[0]! : message.flat());
72
133
  }
73
134
 
74
135
  warn(...message: SlateLogMessageInput[]) {
75
- this.log('warn', message.flat());
136
+ this.log('warn', message.length === 1 ? message[0]! : message.flat());
76
137
  }
77
138
 
78
139
  error(...message: SlateLogMessageInput[]) {
79
- this.log('error', message.flat());
140
+ this.log('error', message.length === 1 ? message[0]! : message.flat());
80
141
  }
81
142
 
82
143
  progress(...message: SlateLogMessageInput[]) {
83
- this.log('progress', message.flat());
144
+ this.log('progress', message.length === 1 ? message[0]! : message.flat());
84
145
  }
85
146
  }
@@ -1,5 +1,5 @@
1
- import { SlateTool, SlateTrigger } from '../action';
2
- import { SlateSpecification } from './specification';
1
+ import type { SlateTool, SlateTrigger } from '../action';
2
+ import type { SlateSpecification } from './specification';
3
3
 
4
4
  export class Slate<ConfigType extends {}, AuthType extends {}> {
5
5
  private constructor(
@@ -1,6 +1,6 @@
1
1
  import z from 'zod';
2
- import { SlateAuth } from '../auth';
3
- import { SlateConfig } from '../config';
2
+ import type { SlateAuth } from '../auth';
3
+ import type { SlateConfig } from '../config';
4
4
 
5
5
  export interface SlateSpecificationParameters {
6
6
  key: string;