@relaycast/engine 8.0.3 → 8.0.5
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/dist/auth/index.d.ts.map +1 -1
- package/dist/auth/index.js +14 -2
- package/dist/auth/index.js.map +1 -1
- package/dist/db/migrations/0035_agent_token_grace.sql +18 -0
- package/dist/db/schema.d.ts +36 -0
- package/dist/db/schema.d.ts.map +1 -1
- package/dist/db/schema.js +5 -0
- package/dist/db/schema.js.map +1 -1
- package/dist/engine/action.d.ts.map +1 -1
- package/dist/engine/action.js +9 -0
- package/dist/engine/action.js.map +1 -1
- package/dist/engine/agent.d.ts.map +1 -1
- package/dist/engine/agent.js +6 -0
- package/dist/engine/agent.js.map +1 -1
- package/dist/engine/node.d.ts +2 -0
- package/dist/engine/node.d.ts.map +1 -1
- package/dist/engine/tokenRotate.d.ts +11 -0
- package/dist/engine/tokenRotate.d.ts.map +1 -1
- package/dist/engine/tokenRotate.js +30 -7
- package/dist/engine/tokenRotate.js.map +1 -1
- package/dist/engine/workspace.d.ts.map +1 -1
- package/dist/engine/workspace.js +124 -15
- package/dist/engine/workspace.js.map +1 -1
- package/dist/engine.js +2 -2
- package/dist/engine.js.map +1 -1
- package/dist/lib/d1Retry.d.ts +17 -0
- package/dist/lib/d1Retry.d.ts.map +1 -0
- package/dist/lib/d1Retry.js +77 -0
- package/dist/lib/d1Retry.js.map +1 -0
- package/dist/lib/httpError.d.ts +6 -5
- package/dist/lib/httpError.d.ts.map +1 -1
- package/dist/lib/httpError.js +27 -5
- package/dist/lib/httpError.js.map +1 -1
- package/dist/ports/database.d.ts +6 -2
- package/dist/ports/database.d.ts.map +1 -1
- package/dist/ports/database.js +7 -2
- package/dist/ports/database.js.map +1 -1
- package/dist/routes/workspace.d.ts.map +1 -1
- package/dist/routes/workspace.js +19 -1
- package/dist/routes/workspace.js.map +1 -1
- package/package.json +3 -3
package/dist/engine/workspace.js
CHANGED
|
@@ -3,6 +3,8 @@ import { workspaces, channels } from '../db/schema.js';
|
|
|
3
3
|
import { randomHex, sha256Hex } from '../lib/crypto.js';
|
|
4
4
|
import { generateId } from './snowflake.js';
|
|
5
5
|
import { codedError } from '../lib/httpError.js';
|
|
6
|
+
import { D1WriteRetryExhaustedError, retryD1Write } from '../lib/d1Retry.js';
|
|
7
|
+
import { runAtomicWrites } from '../ports/database.js';
|
|
6
8
|
function hashApiKey(apiKey) {
|
|
7
9
|
return sha256Hex(apiKey);
|
|
8
10
|
}
|
|
@@ -13,6 +15,64 @@ function buildWorkspaceResponse(workspace, apiKey) {
|
|
|
13
15
|
created_at: workspace.createdAt.toISOString(),
|
|
14
16
|
};
|
|
15
17
|
}
|
|
18
|
+
function isExpectedWorkspacePair(writeResult, expected) {
|
|
19
|
+
const [workspaceRows, channelRows] = writeResult;
|
|
20
|
+
const createdWorkspace = workspaceRows[0];
|
|
21
|
+
const createdChannel = channelRows[0];
|
|
22
|
+
return Boolean(createdWorkspace &&
|
|
23
|
+
createdWorkspace.id === expected.workspaceId &&
|
|
24
|
+
createdWorkspace.name === expected.name &&
|
|
25
|
+
createdWorkspace.apiKeyHash === expected.apiKeyHash &&
|
|
26
|
+
createdChannel &&
|
|
27
|
+
createdChannel.id === expected.channelId &&
|
|
28
|
+
createdChannel.workspaceId === expected.workspaceId &&
|
|
29
|
+
createdChannel.name === 'general');
|
|
30
|
+
}
|
|
31
|
+
async function readCommittedWorkspacePair(db, expected) {
|
|
32
|
+
try {
|
|
33
|
+
const [workspaceRows, channelRows] = await Promise.all([
|
|
34
|
+
db.select().from(workspaces).where(eq(workspaces.id, expected.workspaceId)),
|
|
35
|
+
db.select().from(channels).where(eq(channels.id, expected.channelId)),
|
|
36
|
+
]);
|
|
37
|
+
const result = [workspaceRows, channelRows];
|
|
38
|
+
return isExpectedWorkspacePair(result, expected)
|
|
39
|
+
? { status: 'match', value: result }
|
|
40
|
+
: { status: 'mismatch' };
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
return { status: 'unavailable' };
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
function isUniqueConstraintError(error) {
|
|
47
|
+
let current = error;
|
|
48
|
+
const seen = new Set();
|
|
49
|
+
for (let depth = 0; current !== undefined && depth < 6 && !seen.has(current); depth += 1) {
|
|
50
|
+
seen.add(current);
|
|
51
|
+
if (typeof current === 'string') {
|
|
52
|
+
const message = current.toLowerCase();
|
|
53
|
+
if (message.startsWith('d1_error:') &&
|
|
54
|
+
(message.includes('unique constraint failed') || message.includes('sqlite_constraint_unique'))) {
|
|
55
|
+
return true;
|
|
56
|
+
}
|
|
57
|
+
break;
|
|
58
|
+
}
|
|
59
|
+
if (!current || typeof current !== 'object')
|
|
60
|
+
break;
|
|
61
|
+
const candidate = current;
|
|
62
|
+
const code = typeof candidate.code === 'string' ? candidate.code : '';
|
|
63
|
+
const message = typeof candidate.message === 'string' ? candidate.message.toLowerCase() : '';
|
|
64
|
+
if (code === 'SQLITE_CONSTRAINT_UNIQUE' ||
|
|
65
|
+
code === 'SQLITE_CONSTRAINT_PRIMARYKEY' ||
|
|
66
|
+
(code === 'SQLITE_CONSTRAINT' &&
|
|
67
|
+
(message.includes('unique constraint failed') || message.includes('sqlite_constraint_unique'))) ||
|
|
68
|
+
(message.startsWith('d1_error:') &&
|
|
69
|
+
(message.includes('unique constraint failed') || message.includes('sqlite_constraint_unique')))) {
|
|
70
|
+
return true;
|
|
71
|
+
}
|
|
72
|
+
current = candidate.cause;
|
|
73
|
+
}
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
16
76
|
export async function createWorkspace(db, name, options) {
|
|
17
77
|
const providedOwnerApiKeyHash = typeof options === 'string' ? undefined : options?.ownerApiKeyHash;
|
|
18
78
|
const providedOwnerApiKey = typeof options === 'string' ? options : options?.ownerApiKey;
|
|
@@ -39,22 +99,71 @@ export async function createWorkspace(db, name, options) {
|
|
|
39
99
|
const workspaceId = generateId();
|
|
40
100
|
const apiKey = `rk_live_${randomHex(16)}`;
|
|
41
101
|
const apiKeyHash = await hashApiKey(apiKey);
|
|
42
|
-
const [createdWorkspace] = await db
|
|
43
|
-
.insert(workspaces)
|
|
44
|
-
.values({
|
|
45
|
-
id: workspaceId,
|
|
46
|
-
name,
|
|
47
|
-
apiKeyHash,
|
|
48
|
-
})
|
|
49
|
-
.returning();
|
|
50
|
-
// Auto-create #general channel
|
|
51
102
|
const channelId = generateId();
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
103
|
+
const expected = { workspaceId, name, apiKeyHash, channelId };
|
|
104
|
+
let writeResult;
|
|
105
|
+
try {
|
|
106
|
+
writeResult = await retryD1Write(async () => {
|
|
107
|
+
try {
|
|
108
|
+
return await runAtomicWrites(db, (writeDb) => [
|
|
109
|
+
writeDb
|
|
110
|
+
.insert(workspaces)
|
|
111
|
+
.values({ id: workspaceId, name, apiKeyHash })
|
|
112
|
+
.returning(),
|
|
113
|
+
writeDb
|
|
114
|
+
.insert(channels)
|
|
115
|
+
.values({
|
|
116
|
+
id: channelId,
|
|
117
|
+
workspaceId,
|
|
118
|
+
name: 'general',
|
|
119
|
+
topic: 'General discussion',
|
|
120
|
+
})
|
|
121
|
+
.returning(),
|
|
122
|
+
], { requireAtomic: true });
|
|
123
|
+
}
|
|
124
|
+
catch (cause) {
|
|
125
|
+
if (!isUniqueConstraintError(cause))
|
|
126
|
+
throw cause;
|
|
127
|
+
// A prior attempt may have committed before its response was lost.
|
|
128
|
+
// Accept only that exact generated pair; mismatched IDs fail closed.
|
|
129
|
+
const committed = await readCommittedWorkspacePair(db, expected);
|
|
130
|
+
if (committed.status === 'match')
|
|
131
|
+
return committed.value;
|
|
132
|
+
if (committed.status === 'unavailable') {
|
|
133
|
+
const error = codedError('Workspace storage temporarily unavailable', 'workspace_storage_unavailable', 503);
|
|
134
|
+
error.diagnostics = {
|
|
135
|
+
operation: 'workspace.create',
|
|
136
|
+
storage_error: 'readback_unavailable',
|
|
137
|
+
};
|
|
138
|
+
throw error;
|
|
139
|
+
}
|
|
140
|
+
throw codedError('Generated workspace identifier collision', 'workspace_id_collision', 500);
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
catch (cause) {
|
|
145
|
+
if (!(cause instanceof D1WriteRetryExhaustedError))
|
|
146
|
+
throw cause;
|
|
147
|
+
const committed = await readCommittedWorkspacePair(db, expected);
|
|
148
|
+
if (committed.status === 'match') {
|
|
149
|
+
writeResult = committed.value;
|
|
150
|
+
}
|
|
151
|
+
else {
|
|
152
|
+
const error = codedError('Workspace storage temporarily unavailable', 'workspace_storage_unavailable', 503);
|
|
153
|
+
error.diagnostics = {
|
|
154
|
+
attempts: cause.attempts,
|
|
155
|
+
operation: 'workspace.create',
|
|
156
|
+
storage_error: cause.storageError,
|
|
157
|
+
};
|
|
158
|
+
throw error;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
const [workspaceRows] = writeResult;
|
|
162
|
+
const createdWorkspace = workspaceRows[0];
|
|
163
|
+
// A conflict can only be an idempotent replay of this exact generated pair.
|
|
164
|
+
if (!isExpectedWorkspacePair(writeResult, expected) || !createdWorkspace) {
|
|
165
|
+
throw codedError('Generated workspace identifier collision', 'workspace_id_collision', 500);
|
|
166
|
+
}
|
|
58
167
|
const workspace = buildWorkspaceResponse(createdWorkspace, apiKey);
|
|
59
168
|
return {
|
|
60
169
|
workspace,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workspace.js","sourceRoot":"","sources":["../../src/engine/workspace.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,EAAE,EAAE,MAAM,aAAa,CAAC;AAEtC,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AACvD,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACxD,OAAO,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAC5C,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"workspace.js","sourceRoot":"","sources":["../../src/engine/workspace.ts"],"names":[],"mappings":"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"}
|
package/dist/engine.js
CHANGED
|
@@ -4,7 +4,7 @@ import { secureHeaders } from 'hono/secure-headers';
|
|
|
4
4
|
import { engineContext } from './middleware/engine-context.js';
|
|
5
5
|
import { loggerMiddleware } from './middleware/logger.js';
|
|
6
6
|
import { getRequestLogger, toErrorDetails } from './lib/logger.js';
|
|
7
|
-
import { asCodedError } from './lib/httpError.js';
|
|
7
|
+
import { asCodedError, safeClientErrorMessage } from './lib/httpError.js';
|
|
8
8
|
import { jsonError, jsonMalformedBody, jsonNotFound } from './lib/httpResponse.js';
|
|
9
9
|
import { requiredOriginInfo } from './lib/origin.js';
|
|
10
10
|
import { emitServerEvent } from './lib/serverTelemetry.js';
|
|
@@ -212,7 +212,7 @@ export function createEngine(deps) {
|
|
|
212
212
|
if (error.code === 'invalid_json' || err instanceof SyntaxError) {
|
|
213
213
|
return jsonMalformedBody(c);
|
|
214
214
|
}
|
|
215
|
-
return jsonError(c, error.code || 'internal_error', error
|
|
215
|
+
return jsonError(c, error.code || 'internal_error', safeClientErrorMessage(error), status);
|
|
216
216
|
});
|
|
217
217
|
return app;
|
|
218
218
|
}
|
package/dist/engine.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"engine.js","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAE5B,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAGpD,OAAO,EAAE,aAAa,EAAE,MAAM,gCAAgC,CAAC;AAC/D,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACnE,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"engine.js","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
declare const RETRYABLE_D1_ERRORS: readonly [readonly ["network_connection_lost", "Network connection lost"], readonly ["code_update_reset", "D1 DB reset because its code was updated"], readonly ["startup_storage_reset", "Internal error while starting up D1 DB storage caused object to be reset"], readonly ["storage_reset", "Internal error in D1 DB storage caused object to be reset"], readonly ["remote_node_transient", "Cannot resolve D1 DB due to transient issue on remote node"], readonly ["client_disconnected", "Can't read from request stream because client disconnected"], readonly ["queue_timeout", "D1 DB is overloaded. Requests queued for too long"], readonly ["queue_full", "D1 DB is overloaded. Too many requests queued"]];
|
|
2
|
+
export type RetryableD1ErrorCode = (typeof RETRYABLE_D1_ERRORS)[number][0];
|
|
3
|
+
export declare class D1WriteRetryExhaustedError extends Error {
|
|
4
|
+
readonly attempts: number;
|
|
5
|
+
readonly storageError: RetryableD1ErrorCode;
|
|
6
|
+
constructor(attempts: number, storageError: RetryableD1ErrorCode);
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Classify only D1's own error message (including a nested Drizzle cause).
|
|
10
|
+
* The outer Drizzle message contains SQL and bound parameters, so matching it
|
|
11
|
+
* could mistake a user-controlled value for a retryable storage failure.
|
|
12
|
+
*/
|
|
13
|
+
export declare function retryableD1ErrorCode(error: unknown): RetryableD1ErrorCode | undefined;
|
|
14
|
+
/** Retry a D1 write only for Cloudflare's documented transient error classes. */
|
|
15
|
+
export declare function retryD1Write<T>(write: () => Promise<T>, maxAttempts?: number): Promise<T>;
|
|
16
|
+
export {};
|
|
17
|
+
//# sourceMappingURL=d1Retry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"d1Retry.d.ts","sourceRoot":"","sources":["../../src/lib/d1Retry.ts"],"names":[],"mappings":"AAAA,QAAA,MAAM,mBAAmB,8rBASf,CAAC;AAEX,MAAM,MAAM,oBAAoB,GAAG,CAAC,OAAO,mBAAmB,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;AAE3E,qBAAa,0BAA2B,SAAQ,KAAK;IAEjD,QAAQ,CAAC,QAAQ,EAAE,MAAM;IACzB,QAAQ,CAAC,YAAY,EAAE,oBAAoB;gBADlC,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,oBAAoB;CAK9C;AAcD;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,OAAO,GAAG,oBAAoB,GAAG,SAAS,CAgBrF;AAWD,iFAAiF;AACjF,wBAAsB,YAAY,CAAC,CAAC,EAClC,KAAK,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EACvB,WAAW,SAAI,GACd,OAAO,CAAC,CAAC,CAAC,CAaZ"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
const RETRYABLE_D1_ERRORS = [
|
|
2
|
+
['network_connection_lost', 'Network connection lost'],
|
|
3
|
+
['code_update_reset', 'D1 DB reset because its code was updated'],
|
|
4
|
+
['startup_storage_reset', 'Internal error while starting up D1 DB storage caused object to be reset'],
|
|
5
|
+
['storage_reset', 'Internal error in D1 DB storage caused object to be reset'],
|
|
6
|
+
['remote_node_transient', 'Cannot resolve D1 DB due to transient issue on remote node'],
|
|
7
|
+
['client_disconnected', "Can't read from request stream because client disconnected"],
|
|
8
|
+
['queue_timeout', 'D1 DB is overloaded. Requests queued for too long'],
|
|
9
|
+
['queue_full', 'D1 DB is overloaded. Too many requests queued'],
|
|
10
|
+
];
|
|
11
|
+
export class D1WriteRetryExhaustedError extends Error {
|
|
12
|
+
attempts;
|
|
13
|
+
storageError;
|
|
14
|
+
constructor(attempts, storageError) {
|
|
15
|
+
super('Transient D1 write failed after retry attempts');
|
|
16
|
+
this.attempts = attempts;
|
|
17
|
+
this.storageError = storageError;
|
|
18
|
+
this.name = 'D1WriteRetryExhaustedError';
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
function errorCause(error) {
|
|
22
|
+
if (!error || typeof error !== 'object' || !('cause' in error))
|
|
23
|
+
return undefined;
|
|
24
|
+
return error.cause;
|
|
25
|
+
}
|
|
26
|
+
function errorMessage(error) {
|
|
27
|
+
if (typeof error === 'string')
|
|
28
|
+
return error;
|
|
29
|
+
if (!error || typeof error !== 'object' || !('message' in error))
|
|
30
|
+
return undefined;
|
|
31
|
+
const message = error.message;
|
|
32
|
+
return typeof message === 'string' ? message : undefined;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Classify only D1's own error message (including a nested Drizzle cause).
|
|
36
|
+
* The outer Drizzle message contains SQL and bound parameters, so matching it
|
|
37
|
+
* could mistake a user-controlled value for a retryable storage failure.
|
|
38
|
+
*/
|
|
39
|
+
export function retryableD1ErrorCode(error) {
|
|
40
|
+
let current = error;
|
|
41
|
+
const seen = new Set();
|
|
42
|
+
for (let depth = 0; current !== undefined && depth < 6 && !seen.has(current); depth += 1) {
|
|
43
|
+
seen.add(current);
|
|
44
|
+
const message = errorMessage(current);
|
|
45
|
+
if (message?.startsWith('D1_ERROR:')) {
|
|
46
|
+
const match = RETRYABLE_D1_ERRORS.find(([, fragment]) => message.includes(fragment));
|
|
47
|
+
return match?.[0];
|
|
48
|
+
}
|
|
49
|
+
current = errorCause(current);
|
|
50
|
+
}
|
|
51
|
+
return undefined;
|
|
52
|
+
}
|
|
53
|
+
function retryDelayMs(completedAttempts) {
|
|
54
|
+
const exponential = 50 * (2 ** (completedAttempts - 1));
|
|
55
|
+
return exponential + Math.floor(Math.random() * exponential);
|
|
56
|
+
}
|
|
57
|
+
function sleep(ms) {
|
|
58
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
59
|
+
}
|
|
60
|
+
/** Retry a D1 write only for Cloudflare's documented transient error classes. */
|
|
61
|
+
export async function retryD1Write(write, maxAttempts = 5) {
|
|
62
|
+
for (let attempt = 1;; attempt += 1) {
|
|
63
|
+
try {
|
|
64
|
+
return await write();
|
|
65
|
+
}
|
|
66
|
+
catch (error) {
|
|
67
|
+
const code = retryableD1ErrorCode(error);
|
|
68
|
+
if (!code || attempt >= maxAttempts) {
|
|
69
|
+
if (code)
|
|
70
|
+
throw new D1WriteRetryExhaustedError(attempt, code);
|
|
71
|
+
throw error;
|
|
72
|
+
}
|
|
73
|
+
await sleep(retryDelayMs(attempt));
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=d1Retry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"d1Retry.js","sourceRoot":"","sources":["../../src/lib/d1Retry.ts"],"names":[],"mappings":"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"}
|
package/dist/lib/httpError.d.ts
CHANGED
|
@@ -8,6 +8,8 @@ import type { Context } from 'hono';
|
|
|
8
8
|
export interface CodedError extends Error {
|
|
9
9
|
code?: string;
|
|
10
10
|
status?: number;
|
|
11
|
+
diagnostics?: Record<string, unknown>;
|
|
12
|
+
clientSafe?: true;
|
|
11
13
|
}
|
|
12
14
|
/**
|
|
13
15
|
* Narrow an unknown thrown value to a {@link CodedError} without asserting.
|
|
@@ -18,9 +20,9 @@ export interface CodedError extends Error {
|
|
|
18
20
|
export declare function asCodedError(err: unknown): CodedError;
|
|
19
21
|
/** Build an `Error` carrying a stable `code` and HTTP `status` (no cast at the call site). */
|
|
20
22
|
export declare function codedError(message: string, code: string, status: number): CodedError;
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
23
|
+
/** Select primitive, explicitly allowlisted fields for logs and telemetry. */
|
|
24
|
+
export declare function safeErrorDiagnostics(error: CodedError): Record<string, string | number | boolean>;
|
|
25
|
+
export declare function safeClientErrorMessage(error: CodedError, status?: number): string;
|
|
24
26
|
/**
|
|
25
27
|
* Render a thrown value as the standard `{ ok: false, error: { code, message } }`
|
|
26
28
|
* envelope with the carried (or default 500) status. Centralizes the single
|
|
@@ -28,12 +30,11 @@ interface ErrorResponseOptions {
|
|
|
28
30
|
* real `instanceof` narrowing instead of the unchecked `err as Error & {...}`
|
|
29
31
|
* assertion that was duplicated across every route handler.
|
|
30
32
|
*/
|
|
31
|
-
export declare function errorResponse(c: Context, err: unknown
|
|
33
|
+
export declare function errorResponse(c: Context, err: unknown): Response & import("hono").TypedResponse<{
|
|
32
34
|
ok: false;
|
|
33
35
|
error: {
|
|
34
36
|
code: string;
|
|
35
37
|
message: string;
|
|
36
38
|
};
|
|
37
39
|
}, 300 | 100 | 102 | 103 | 200 | 201 | 202 | 203 | 206 | 207 | 208 | 226 | 301 | 302 | 303 | 305 | 306 | 307 | 308 | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 421 | 422 | 423 | 424 | 425 | 426 | 428 | 429 | 431 | 451 | 500 | 501 | 502 | 503 | 504 | 505 | 506 | 507 | 508 | 510 | 511 | -1, "json">;
|
|
38
|
-
export {};
|
|
39
40
|
//# sourceMappingURL=httpError.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"httpError.d.ts","sourceRoot":"","sources":["../../src/lib/httpError.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AAIpC;;;;;GAKG;AACH,MAAM,WAAW,UAAW,SAAQ,KAAK;IACvC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"httpError.d.ts","sourceRoot":"","sources":["../../src/lib/httpError.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AAIpC;;;;;GAKG;AACH,MAAM,WAAW,UAAW,SAAQ,KAAK;IACvC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACtC,UAAU,CAAC,EAAE,IAAI,CAAC;CACnB;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,OAAO,GAAG,UAAU,CASrD;AAED,8FAA8F;AAC9F,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,UAAU,CAEpF;AAQD,8EAA8E;AAC9E,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,CAWjG;AAMD,wBAAgB,sBAAsB,CACpC,KAAK,EAAE,UAAU,EACjB,MAAM,SAA6B,GAClC,MAAM,CAIR;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAAE,OAAO,EAAE,GAAG,EAAE,OAAO;;;;;;iXAYrD"}
|
package/dist/lib/httpError.js
CHANGED
|
@@ -18,10 +18,31 @@ export function asCodedError(err) {
|
|
|
18
18
|
}
|
|
19
19
|
/** Build an `Error` carrying a stable `code` and HTTP `status` (no cast at the call site). */
|
|
20
20
|
export function codedError(message, code, status) {
|
|
21
|
-
return Object.assign(new Error(message), { code, status });
|
|
21
|
+
return Object.assign(new Error(message), { code, status, clientSafe: true });
|
|
22
22
|
}
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
const SAFE_DIAGNOSTIC_FIELDS = new Set([
|
|
24
|
+
'attempts',
|
|
25
|
+
'operation',
|
|
26
|
+
'storage_error',
|
|
27
|
+
]);
|
|
28
|
+
/** Select primitive, explicitly allowlisted fields for logs and telemetry. */
|
|
29
|
+
export function safeErrorDiagnostics(error) {
|
|
30
|
+
const safe = {};
|
|
31
|
+
for (const [key, value] of Object.entries(error.diagnostics ?? {})) {
|
|
32
|
+
if (SAFE_DIAGNOSTIC_FIELDS.has(key) &&
|
|
33
|
+
(typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean')) {
|
|
34
|
+
safe[key] = value;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return safe;
|
|
38
|
+
}
|
|
39
|
+
function effectiveHttpStatus(error) {
|
|
40
|
+
return error.status || 500;
|
|
41
|
+
}
|
|
42
|
+
export function safeClientErrorMessage(error, status = effectiveHttpStatus(error)) {
|
|
43
|
+
return status >= 500 && error.clientSafe !== true && !error.code
|
|
44
|
+
? 'Internal server error'
|
|
45
|
+
: error.message;
|
|
25
46
|
}
|
|
26
47
|
/**
|
|
27
48
|
* Render a thrown value as the standard `{ ok: false, error: { code, message } }`
|
|
@@ -30,11 +51,12 @@ function messageWithCause(error) {
|
|
|
30
51
|
* real `instanceof` narrowing instead of the unchecked `err as Error & {...}`
|
|
31
52
|
* assertion that was duplicated across every route handler.
|
|
32
53
|
*/
|
|
33
|
-
export function errorResponse(c, err
|
|
54
|
+
export function errorResponse(c, err) {
|
|
34
55
|
const error = asCodedError(err);
|
|
35
56
|
if (err instanceof SyntaxError) {
|
|
36
57
|
return jsonMalformedBody(c);
|
|
37
58
|
}
|
|
38
|
-
|
|
59
|
+
const status = effectiveHttpStatus(error);
|
|
60
|
+
return jsonError(c, error.code || 'internal_error', safeClientErrorMessage(error, status), status);
|
|
39
61
|
}
|
|
40
62
|
//# sourceMappingURL=httpError.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"httpError.js","sourceRoot":"","sources":["../../src/lib/httpError.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,SAAS,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"httpError.js","sourceRoot":"","sources":["../../src/lib/httpError.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,SAAS,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAejE;;;;;GAKG;AACH,MAAM,UAAU,YAAY,CAAC,GAAY;IACvC,IAAI,GAAG,YAAY,KAAK;QAAE,OAAO,GAAiB,CAAC;IACnD,IAAI,GAAG,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;QAC1D,MAAM,OAAO,GAAG,SAAS,IAAI,GAAG,CAAC,CAAC,CAAC,MAAM,CAAE,GAA4B,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,eAAe,CAAC;QACnG,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,OAAO,CAAC,EAAE,GAAG,CAAC,CAAC;QACrD,KAAK,CAAC,OAAO,GAAG,OAAO,CAAC;QACxB,OAAO,KAAmB,CAAC;IAC7B,CAAC;IACD,OAAO,IAAI,KAAK,CAAC,OAAO,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC;AACpE,CAAC;AAED,8FAA8F;AAC9F,MAAM,UAAU,UAAU,CAAC,OAAe,EAAE,IAAY,EAAE,MAAc;IACtE,OAAO,MAAM,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,OAAO,CAAC,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,IAAa,EAAE,CAAC,CAAC;AACxF,CAAC;AAED,MAAM,sBAAsB,GAAG,IAAI,GAAG,CAAC;IACrC,UAAU;IACV,WAAW;IACX,eAAe;CAChB,CAAC,CAAC;AAEH,8EAA8E;AAC9E,MAAM,UAAU,oBAAoB,CAAC,KAAiB;IACpD,MAAM,IAAI,GAA8C,EAAE,CAAC;IAC3D,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,WAAW,IAAI,EAAE,CAAC,EAAE,CAAC;QACnE,IACE,sBAAsB,CAAC,GAAG,CAAC,GAAG,CAAC;YAC/B,CAAC,OAAO,KAAK,KAAK,QAAQ,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,OAAO,KAAK,KAAK,SAAS,CAAC,EACtF,CAAC;YACD,IAAI,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;QACpB,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,SAAS,mBAAmB,CAAC,KAAiB;IAC5C,OAAO,KAAK,CAAC,MAAM,IAAI,GAAG,CAAC;AAC7B,CAAC;AAED,MAAM,UAAU,sBAAsB,CACpC,KAAiB,EACjB,MAAM,GAAG,mBAAmB,CAAC,KAAK,CAAC;IAEnC,OAAO,MAAM,IAAI,GAAG,IAAI,KAAK,CAAC,UAAU,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI;QAC9D,CAAC,CAAC,uBAAuB;QACzB,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC;AACpB,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,aAAa,CAAC,CAAU,EAAE,GAAY;IACpD,MAAM,KAAK,GAAG,YAAY,CAAC,GAAG,CAAC,CAAC;IAChC,IAAI,GAAG,YAAY,WAAW,EAAE,CAAC;QAC/B,OAAO,iBAAiB,CAAC,CAAC,CAAC,CAAC;IAC9B,CAAC;IACD,MAAM,MAAM,GAAG,mBAAmB,CAAC,KAAK,CAAC,CAAC;IAC1C,OAAO,SAAS,CACd,CAAC,EACD,KAAK,CAAC,IAAI,IAAI,gBAAgB,EAC9B,sBAAsB,CAAC,KAAK,EAAE,MAAM,CAAC,EACrC,MAA8B,CAC/B,CAAC;AACJ,CAAC"}
|
package/dist/ports/database.d.ts
CHANGED
|
@@ -59,6 +59,10 @@ export type AtomicWrite = BatchItem<'sqlite'> & PromiseLike<unknown>;
|
|
|
59
59
|
*/
|
|
60
60
|
export type AtomicWriteBuilder = (db: EngineDb) => readonly AtomicWrite[];
|
|
61
61
|
export type AtomicWriteInput = readonly AtomicWrite[] | AtomicWriteBuilder;
|
|
62
|
+
export interface RunAtomicWritesOptions {
|
|
63
|
+
/** Reject before building statements when the handle cannot guarantee rollback. */
|
|
64
|
+
requireAtomic?: boolean;
|
|
65
|
+
}
|
|
62
66
|
/**
|
|
63
67
|
* D1-style atomic batch: every statement applies, or none does.
|
|
64
68
|
*
|
|
@@ -87,12 +91,12 @@ export declare function runAtomic<T>(db: EngineDb, fn: (tx: EngineDb) => Promise
|
|
|
87
91
|
* statements are built from the original handle and run as one atomic
|
|
88
92
|
* batch.
|
|
89
93
|
* 3. Neither — statements run sequentially with no rollback, the engine's
|
|
90
|
-
* historical behavior for bare handles.
|
|
94
|
+
* historical behavior for bare handles, unless `requireAtomic` is set.
|
|
91
95
|
*
|
|
92
96
|
* Returns the per-statement results in order, so callers can recover
|
|
93
97
|
* `.returning()` rows by index. Statements must not depend on each other's
|
|
94
98
|
* DB-returned values (IDs are app-generated snowflakes), because under a batch
|
|
95
99
|
* nothing is visible until every statement has executed.
|
|
96
100
|
*/
|
|
97
|
-
export declare function runAtomicWrites(db: EngineDb, input: AtomicWriteInput): Promise<unknown[]>;
|
|
101
|
+
export declare function runAtomicWrites(db: EngineDb, input: AtomicWriteInput, options?: RunAtomicWritesOptions): Promise<unknown[]>;
|
|
98
102
|
//# sourceMappingURL=database.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"database.d.ts","sourceRoot":"","sources":["../../src/ports/database.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAClE,OAAO,KAAK,KAAK,MAAM,MAAM,iBAAiB,CAAC;AAE/C;;;;;;;;;;;;;;;;;;;;GAoBG;AAkBH,MAAM,MAAM,QAAQ,CAAC,UAAU,GAAG,OAAO,IAAI,kBAAkB,CAAC,OAAO,EAAE,UAAU,EAAE,OAAO,MAAM,CAAC,CAAC;AAEpG;;;;;;;;;;;;;;GAcG;AACH,MAAM,WAAW,qBAAqB;IACpC,eAAe,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,QAAQ,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;CAClE;AAED;;;;;GAKG;AACH,MAAM,MAAM,WAAW,GAAG,SAAS,CAAC,QAAQ,CAAC,GAAG,WAAW,CAAC,OAAO,CAAC,CAAC;AAErE;;;;;;;;GAQG;AACH,MAAM,MAAM,kBAAkB,GAAG,CAAC,EAAE,EAAE,QAAQ,KAAK,SAAS,WAAW,EAAE,CAAC;AAC1E,MAAM,MAAM,gBAAgB,GAAG,SAAS,WAAW,EAAE,GAAG,kBAAkB,CAAC;AAE3E;;;;;;;GAOG;AACH,MAAM,WAAW,eAAe;IAC9B,KAAK,CAAC,UAAU,EAAE,SAAS,CAAC,WAAW,EAAE,GAAG,WAAW,EAAE,CAAC,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC;CACjF;AAcD;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,CAAC,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,QAAQ,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAGvF;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,eAAe,CACnC,EAAE,EAAE,QAAQ,EACZ,KAAK,EAAE,gBAAgB,
|
|
1
|
+
{"version":3,"file":"database.d.ts","sourceRoot":"","sources":["../../src/ports/database.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAClE,OAAO,KAAK,KAAK,MAAM,MAAM,iBAAiB,CAAC;AAE/C;;;;;;;;;;;;;;;;;;;;GAoBG;AAkBH,MAAM,MAAM,QAAQ,CAAC,UAAU,GAAG,OAAO,IAAI,kBAAkB,CAAC,OAAO,EAAE,UAAU,EAAE,OAAO,MAAM,CAAC,CAAC;AAEpG;;;;;;;;;;;;;;GAcG;AACH,MAAM,WAAW,qBAAqB;IACpC,eAAe,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,QAAQ,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;CAClE;AAED;;;;;GAKG;AACH,MAAM,MAAM,WAAW,GAAG,SAAS,CAAC,QAAQ,CAAC,GAAG,WAAW,CAAC,OAAO,CAAC,CAAC;AAErE;;;;;;;;GAQG;AACH,MAAM,MAAM,kBAAkB,GAAG,CAAC,EAAE,EAAE,QAAQ,KAAK,SAAS,WAAW,EAAE,CAAC;AAC1E,MAAM,MAAM,gBAAgB,GAAG,SAAS,WAAW,EAAE,GAAG,kBAAkB,CAAC;AAE3E,MAAM,WAAW,sBAAsB;IACrC,mFAAmF;IACnF,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,eAAe;IAC9B,KAAK,CAAC,UAAU,EAAE,SAAS,CAAC,WAAW,EAAE,GAAG,WAAW,EAAE,CAAC,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC;CACjF;AAcD;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,CAAC,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,QAAQ,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAGvF;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,eAAe,CACnC,EAAE,EAAE,QAAQ,EACZ,KAAK,EAAE,gBAAgB,EACvB,OAAO,GAAE,sBAA2B,GACnC,OAAO,CAAC,OAAO,EAAE,CAAC,CA0BpB"}
|
package/dist/ports/database.js
CHANGED
|
@@ -28,15 +28,20 @@ export function runAtomic(db, fn) {
|
|
|
28
28
|
* statements are built from the original handle and run as one atomic
|
|
29
29
|
* batch.
|
|
30
30
|
* 3. Neither — statements run sequentially with no rollback, the engine's
|
|
31
|
-
* historical behavior for bare handles.
|
|
31
|
+
* historical behavior for bare handles, unless `requireAtomic` is set.
|
|
32
32
|
*
|
|
33
33
|
* Returns the per-statement results in order, so callers can recover
|
|
34
34
|
* `.returning()` rows by index. Statements must not depend on each other's
|
|
35
35
|
* DB-returned values (IDs are app-generated snowflakes), because under a batch
|
|
36
36
|
* nothing is visible until every statement has executed.
|
|
37
37
|
*/
|
|
38
|
-
export async function runAtomicWrites(db, input) {
|
|
38
|
+
export async function runAtomicWrites(db, input, options = {}) {
|
|
39
39
|
const handle = db;
|
|
40
|
+
if (options.requireAtomic &&
|
|
41
|
+
typeof handle.withTransaction !== 'function' &&
|
|
42
|
+
typeof handle.batch !== 'function') {
|
|
43
|
+
throw new Error('Atomic write capability required: database handle exposes neither withTransaction nor batch');
|
|
44
|
+
}
|
|
40
45
|
if (handle.withTransaction) {
|
|
41
46
|
return handle.withTransaction(async (tx) => {
|
|
42
47
|
const statements = buildStatements(input, tx);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"database.js","sourceRoot":"","sources":["../../src/ports/database.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"database.js","sourceRoot":"","sources":["../../src/ports/database.ts"],"names":[],"mappings":"AAoGA,SAAS,eAAe,CAAC,KAAuB,EAAE,EAAY;IAC5D,OAAO,OAAO,KAAK,KAAK,UAAU,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;AACzD,CAAC;AAED,KAAK,UAAU,eAAe,CAAC,UAAkC;IAC/D,MAAM,OAAO,GAAc,EAAE,CAAC;IAC9B,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE,CAAC;QACnC,OAAO,CAAC,IAAI,CAAC,MAAM,SAAS,CAAC,CAAC;IAChC,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,SAAS,CAAI,EAAY,EAAE,EAAgC;IACzE,MAAM,EAAE,eAAe,EAAE,GAAG,EAA+C,CAAC;IAC5E,OAAO,eAAe,CAAC,CAAC,CAAC,eAAe,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;AACxD,CAAC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,EAAY,EACZ,KAAuB,EACvB,UAAkC,EAAE;IAEpC,MAAM,MAAM,GAAG,EAA0E,CAAC;IAC1F,IACE,OAAO,CAAC,aAAa;QACrB,OAAO,MAAM,CAAC,eAAe,KAAK,UAAU;QAC5C,OAAO,MAAM,CAAC,KAAK,KAAK,UAAU,EAClC,CAAC;QACD,MAAM,IAAI,KAAK,CACb,6FAA6F,CAC9F,CAAC;IACJ,CAAC;IAED,IAAI,MAAM,CAAC,eAAe,EAAE,CAAC;QAC3B,OAAO,MAAM,CAAC,eAAe,CAAC,KAAK,EAAE,EAAE,EAAE,EAAE;YACzC,MAAM,UAAU,GAAG,eAAe,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;YAC9C,OAAO,UAAU,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,eAAe,CAAC,UAAU,CAAC,CAAC;QACpE,CAAC,CAAC,CAAC;IACL,CAAC;IAED,MAAM,UAAU,GAAG,eAAe,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IAC9C,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAEvC,IAAI,OAAO,MAAM,CAAC,KAAK,KAAK,UAAU,EAAE,CAAC;QACvC,OAAO,MAAM,CAAC,KAAK,CAAC,UAA6C,CAAC,CAAC;IACrE,CAAC;IACD,OAAO,eAAe,CAAC,UAAU,CAAC,CAAC;AACrC,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workspace.d.ts","sourceRoot":"","sources":["../../src/routes/workspace.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAI5B,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"workspace.d.ts","sourceRoot":"","sources":["../../src/routes/workspace.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAI5B,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AAqCxC,eAAO,MAAM,eAAe,qDAAqB,CAAC"}
|
package/dist/routes/workspace.js
CHANGED
|
@@ -12,7 +12,8 @@ import { listWorkspaceEvents, WORKSPACE_EVENT_LIST_DEFAULT_LIMIT, WORKSPACE_EVEN
|
|
|
12
12
|
import { channels } from '../db/schema.js';
|
|
13
13
|
import { and, eq, inArray } from 'drizzle-orm';
|
|
14
14
|
import { emitServerEvent } from '../lib/serverTelemetry.js';
|
|
15
|
-
import { errorResponse } from '../lib/httpError.js';
|
|
15
|
+
import { asCodedError, errorResponse, safeErrorDiagnostics } from '../lib/httpError.js';
|
|
16
|
+
import { getRequestLogger } from '../lib/logger.js';
|
|
16
17
|
import { jsonCreated, jsonError, jsonNoContent, jsonNotFound, jsonOk, parseJsonBody, parseQueryParams, } from '../lib/httpResponse.js';
|
|
17
18
|
import { parsePaginationQuery, positiveIntQueryParam } from '../lib/httpQuery.js';
|
|
18
19
|
export const workspaceRoutes = new Hono();
|
|
@@ -125,6 +126,23 @@ workspaceRoutes.post('/workspaces', async (c) => {
|
|
|
125
126
|
return result.created ? jsonCreated(c, result.workspace) : jsonOk(c, result.workspace);
|
|
126
127
|
}
|
|
127
128
|
catch (err) {
|
|
129
|
+
const error = asCodedError(err);
|
|
130
|
+
if ((error.status ?? 500) >= 500) {
|
|
131
|
+
const diagnostics = safeErrorDiagnostics(error);
|
|
132
|
+
getRequestLogger(c, 'workspace.create').error('Workspace creation failed', {
|
|
133
|
+
error_code: error.code ?? 'internal_error',
|
|
134
|
+
error_status: error.status ?? 500,
|
|
135
|
+
...diagnostics,
|
|
136
|
+
});
|
|
137
|
+
c.get('engine').telemetry.captureException(new Error(error.code ?? 'internal_error'), {
|
|
138
|
+
path: c.req.path,
|
|
139
|
+
method: c.req.method,
|
|
140
|
+
status_code: error.status ?? 500,
|
|
141
|
+
error_code: error.code ?? 'internal_error',
|
|
142
|
+
request_id: c.get('requestId'),
|
|
143
|
+
...diagnostics,
|
|
144
|
+
});
|
|
145
|
+
}
|
|
128
146
|
return errorResponse(c, err);
|
|
129
147
|
}
|
|
130
148
|
});
|