@shipfox/api-logs 4.0.0 → 6.0.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.
- package/.turbo/turbo-build.log +4 -4
- package/CHANGELOG.md +52 -0
- package/dist/config.d.ts +1 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +5 -10
- package/dist/config.js.map +1 -1
- package/dist/core/append-logs.d.ts +2 -6
- package/dist/core/append-logs.d.ts.map +1 -1
- package/dist/core/append-logs.js +7 -16
- package/dist/core/append-logs.js.map +1 -1
- package/dist/index.d.ts +5 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +63 -59
- package/dist/index.js.map +1 -1
- package/dist/presentation/routes/append-logs.d.ts +2 -1
- package/dist/presentation/routes/append-logs.d.ts.map +1 -1
- package/dist/presentation/routes/append-logs.js +61 -61
- package/dist/presentation/routes/append-logs.js.map +1 -1
- package/dist/presentation/routes/index.d.ts +2 -1
- package/dist/presentation/routes/index.d.ts.map +1 -1
- package/dist/presentation/routes/index.js +25 -23
- package/dist/presentation/routes/index.js.map +1 -1
- package/dist/tsconfig.test.tsbuildinfo +1 -1
- package/package.json +27 -34
- package/src/config.test.ts +8 -32
- package/src/config.ts +10 -15
- package/src/core/append-logs.test.ts +13 -19
- package/src/core/append-logs.ts +12 -20
- package/src/index.ts +63 -51
- package/src/presentation/routes/append-logs.test.ts +3 -2
- package/src/presentation/routes/append-logs.ts +54 -50
- package/src/presentation/routes/index.ts +22 -19
- package/src/presentation/routes/read-logs.test.ts +3 -2
- package/test/env.ts +1 -3
- package/test/fixtures/lease-token.ts +2 -1
- package/test/fixtures/workflows-client.ts +17 -0
- package/tsconfig.build.tsbuildinfo +1 -1
|
@@ -5,67 +5,67 @@ import { ClientError, defineRoute } from '@shipfox/node-fastify';
|
|
|
5
5
|
import { z } from 'zod';
|
|
6
6
|
import { appendLogs } from '#core/append-logs.js';
|
|
7
7
|
import { LeaseStreamMismatchError, MalformedLogChunkError, OffsetGapError } from '#core/errors.js';
|
|
8
|
-
export
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
8
|
+
export function createAppendLogsRoute(workflows) {
|
|
9
|
+
return defineRoute({
|
|
10
|
+
method: 'POST',
|
|
11
|
+
path: '/steps/:stepId/logs',
|
|
12
|
+
description: 'Append a chunk of logs for a step attempt of the leased job.',
|
|
13
|
+
schema: {
|
|
14
|
+
params: z.object({
|
|
15
|
+
stepId: z.string().uuid()
|
|
16
|
+
}),
|
|
17
|
+
querystring: appendLogsQuerySchema,
|
|
18
|
+
response: {
|
|
19
|
+
200: appendLogsResponseSchema,
|
|
20
|
+
409: offsetGapResponseSchema
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
errorHandler: (error)=>{
|
|
24
|
+
if (error instanceof OffsetGapError) {
|
|
25
|
+
throw new ClientError('Append offset is ahead of the committed length', 'offset-gap', {
|
|
26
|
+
status: 409,
|
|
27
|
+
details: {
|
|
28
|
+
committed_length: error.committedLength
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
if (error instanceof MalformedLogChunkError) {
|
|
33
|
+
throw new ClientError(error.message, 'malformed-log-chunk', {
|
|
34
|
+
status: 400
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
if (error instanceof LeaseStreamMismatchError) {
|
|
38
|
+
throw new ClientError(error.message, 'lease-stream-mismatch', {
|
|
39
|
+
status: 403
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
throw error;
|
|
43
|
+
},
|
|
44
|
+
handler: async (request)=>{
|
|
45
|
+
const leasedJob = requireLeasedJobContext(request);
|
|
46
|
+
const { stepId } = request.params;
|
|
47
|
+
const { attempt, offset } = request.query;
|
|
48
|
+
if (leasedJob.currentStepId !== stepId || leasedJob.currentStepAttempt !== attempt) {
|
|
49
|
+
throw new ClientError('Step not found for leased job execution', 'step-not-found', {
|
|
50
|
+
status: 404
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
const result = await appendLogs({
|
|
54
|
+
jobId: leasedJob.jobId,
|
|
55
|
+
workspaceId: leasedJob.workspaceId,
|
|
56
|
+
projectId: leasedJob.projectId,
|
|
57
|
+
workflowRunAttemptId: leasedJob.workflowRunAttemptId,
|
|
58
|
+
stepId,
|
|
59
|
+
attempt,
|
|
60
|
+
offset,
|
|
61
|
+
body: request.body ?? Buffer.alloc(0)
|
|
62
|
+
}, workflows);
|
|
63
|
+
return {
|
|
64
|
+
committed_length: result.committedLength,
|
|
65
|
+
capped: result.capped
|
|
66
|
+
};
|
|
20
67
|
}
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
if (error instanceof OffsetGapError) {
|
|
24
|
-
throw new ClientError('Append offset is ahead of the committed length', 'offset-gap', {
|
|
25
|
-
status: 409,
|
|
26
|
-
details: {
|
|
27
|
-
committed_length: error.committedLength
|
|
28
|
-
}
|
|
29
|
-
});
|
|
30
|
-
}
|
|
31
|
-
if (error instanceof MalformedLogChunkError) {
|
|
32
|
-
throw new ClientError(error.message, 'malformed-log-chunk', {
|
|
33
|
-
status: 400
|
|
34
|
-
});
|
|
35
|
-
}
|
|
36
|
-
if (error instanceof LeaseStreamMismatchError) {
|
|
37
|
-
throw new ClientError(error.message, 'lease-stream-mismatch', {
|
|
38
|
-
status: 403
|
|
39
|
-
});
|
|
40
|
-
}
|
|
41
|
-
throw error;
|
|
42
|
-
},
|
|
43
|
-
handler: async (request)=>{
|
|
44
|
-
const leasedJob = requireLeasedJobContext(request);
|
|
45
|
-
const { stepId } = request.params;
|
|
46
|
-
const { attempt, offset } = request.query;
|
|
47
|
-
// The scoped lease proves membership in the dispatched step attempt; active-step
|
|
48
|
-
// completion remains governed by workflow/report state, not the log append route.
|
|
49
|
-
if (leasedJob.currentStepId !== stepId || leasedJob.currentStepAttempt !== attempt) {
|
|
50
|
-
throw new ClientError('Step not found for leased job execution', 'step-not-found', {
|
|
51
|
-
status: 404
|
|
52
|
-
});
|
|
53
|
-
}
|
|
54
|
-
const result = await appendLogs({
|
|
55
|
-
jobId: leasedJob.jobId,
|
|
56
|
-
workspaceId: leasedJob.workspaceId,
|
|
57
|
-
projectId: leasedJob.projectId,
|
|
58
|
-
workflowRunAttemptId: leasedJob.workflowRunAttemptId,
|
|
59
|
-
stepId,
|
|
60
|
-
attempt,
|
|
61
|
-
offset,
|
|
62
|
-
body: request.body ?? Buffer.alloc(0)
|
|
63
|
-
});
|
|
64
|
-
return {
|
|
65
|
-
committed_length: result.committedLength,
|
|
66
|
-
capped: result.capped
|
|
67
|
-
};
|
|
68
|
-
}
|
|
69
|
-
});
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
70
|
|
|
71
71
|
//# sourceMappingURL=append-logs.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/presentation/routes/append-logs.ts"],"sourcesContent":["import {Buffer} from 'node:buffer';\nimport {requireLeasedJobContext} from '@shipfox/api-auth-context';\nimport {\n appendLogsQuerySchema,\n appendLogsResponseSchema,\n offsetGapResponseSchema,\n} from '@shipfox/api-logs-dto';\nimport {ClientError, defineRoute} from '@shipfox/node-fastify';\nimport {z} from 'zod';\nimport {appendLogs} from '#core/append-logs.js';\nimport {LeaseStreamMismatchError, MalformedLogChunkError, OffsetGapError} from '#core/errors.js';\n\nexport
|
|
1
|
+
{"version":3,"sources":["../../../src/presentation/routes/append-logs.ts"],"sourcesContent":["import {Buffer} from 'node:buffer';\nimport {requireLeasedJobContext} from '@shipfox/api-auth-context';\nimport {\n appendLogsQuerySchema,\n appendLogsResponseSchema,\n offsetGapResponseSchema,\n} from '@shipfox/api-logs-dto';\nimport type {WorkflowsModuleClient} from '@shipfox/api-workflows-dto/inter-module';\nimport {ClientError, defineRoute} from '@shipfox/node-fastify';\nimport {z} from 'zod';\nimport {appendLogs} from '#core/append-logs.js';\nimport {LeaseStreamMismatchError, MalformedLogChunkError, OffsetGapError} from '#core/errors.js';\n\nexport function createAppendLogsRoute(workflows: WorkflowsModuleClient) {\n return defineRoute({\n method: 'POST',\n path: '/steps/:stepId/logs',\n description: 'Append a chunk of logs for a step attempt of the leased job.',\n schema: {\n params: z.object({stepId: z.string().uuid()}),\n querystring: appendLogsQuerySchema,\n response: {\n 200: appendLogsResponseSchema,\n 409: offsetGapResponseSchema,\n },\n },\n errorHandler: (error) => {\n if (error instanceof OffsetGapError) {\n throw new ClientError('Append offset is ahead of the committed length', 'offset-gap', {\n status: 409,\n details: {committed_length: error.committedLength},\n });\n }\n if (error instanceof MalformedLogChunkError) {\n throw new ClientError(error.message, 'malformed-log-chunk', {status: 400});\n }\n if (error instanceof LeaseStreamMismatchError) {\n throw new ClientError(error.message, 'lease-stream-mismatch', {status: 403});\n }\n throw error;\n },\n handler: async (request) => {\n const leasedJob = requireLeasedJobContext(request);\n const {stepId} = request.params;\n const {attempt, offset} = request.query;\n\n if (leasedJob.currentStepId !== stepId || leasedJob.currentStepAttempt !== attempt) {\n throw new ClientError('Step not found for leased job execution', 'step-not-found', {\n status: 404,\n });\n }\n\n const result = await appendLogs(\n {\n jobId: leasedJob.jobId,\n workspaceId: leasedJob.workspaceId,\n projectId: leasedJob.projectId,\n workflowRunAttemptId: leasedJob.workflowRunAttemptId,\n stepId,\n attempt,\n offset,\n body: (request.body as Buffer | undefined) ?? Buffer.alloc(0),\n },\n workflows,\n );\n\n return {committed_length: result.committedLength, capped: result.capped};\n },\n });\n}\n"],"names":["Buffer","requireLeasedJobContext","appendLogsQuerySchema","appendLogsResponseSchema","offsetGapResponseSchema","ClientError","defineRoute","z","appendLogs","LeaseStreamMismatchError","MalformedLogChunkError","OffsetGapError","createAppendLogsRoute","workflows","method","path","description","schema","params","object","stepId","string","uuid","querystring","response","errorHandler","error","status","details","committed_length","committedLength","message","handler","request","leasedJob","attempt","offset","query","currentStepId","currentStepAttempt","result","jobId","workspaceId","projectId","workflowRunAttemptId","body","alloc","capped"],"mappings":"AAAA,SAAQA,MAAM,QAAO,cAAc;AACnC,SAAQC,uBAAuB,QAAO,4BAA4B;AAClE,SACEC,qBAAqB,EACrBC,wBAAwB,EACxBC,uBAAuB,QAClB,wBAAwB;AAE/B,SAAQC,WAAW,EAAEC,WAAW,QAAO,wBAAwB;AAC/D,SAAQC,CAAC,QAAO,MAAM;AACtB,SAAQC,UAAU,QAAO,uBAAuB;AAChD,SAAQC,wBAAwB,EAAEC,sBAAsB,EAAEC,cAAc,QAAO,kBAAkB;AAEjG,OAAO,SAASC,sBAAsBC,SAAgC;IACpE,OAAOP,YAAY;QACjBQ,QAAQ;QACRC,MAAM;QACNC,aAAa;QACbC,QAAQ;YACNC,QAAQX,EAAEY,MAAM,CAAC;gBAACC,QAAQb,EAAEc,MAAM,GAAGC,IAAI;YAAE;YAC3CC,aAAarB;YACbsB,UAAU;gBACR,KAAKrB;gBACL,KAAKC;YACP;QACF;QACAqB,cAAc,CAACC;YACb,IAAIA,iBAAiBf,gBAAgB;gBACnC,MAAM,IAAIN,YAAY,kDAAkD,cAAc;oBACpFsB,QAAQ;oBACRC,SAAS;wBAACC,kBAAkBH,MAAMI,eAAe;oBAAA;gBACnD;YACF;YACA,IAAIJ,iBAAiBhB,wBAAwB;gBAC3C,MAAM,IAAIL,YAAYqB,MAAMK,OAAO,EAAE,uBAAuB;oBAACJ,QAAQ;gBAAG;YAC1E;YACA,IAAID,iBAAiBjB,0BAA0B;gBAC7C,MAAM,IAAIJ,YAAYqB,MAAMK,OAAO,EAAE,yBAAyB;oBAACJ,QAAQ;gBAAG;YAC5E;YACA,MAAMD;QACR;QACAM,SAAS,OAAOC;YACd,MAAMC,YAAYjC,wBAAwBgC;YAC1C,MAAM,EAACb,MAAM,EAAC,GAAGa,QAAQf,MAAM;YAC/B,MAAM,EAACiB,OAAO,EAAEC,MAAM,EAAC,GAAGH,QAAQI,KAAK;YAEvC,IAAIH,UAAUI,aAAa,KAAKlB,UAAUc,UAAUK,kBAAkB,KAAKJ,SAAS;gBAClF,MAAM,IAAI9B,YAAY,2CAA2C,kBAAkB;oBACjFsB,QAAQ;gBACV;YACF;YAEA,MAAMa,SAAS,MAAMhC,WACnB;gBACEiC,OAAOP,UAAUO,KAAK;gBACtBC,aAAaR,UAAUQ,WAAW;gBAClCC,WAAWT,UAAUS,SAAS;gBAC9BC,sBAAsBV,UAAUU,oBAAoB;gBACpDxB;gBACAe;gBACAC;gBACAS,MAAM,AAACZ,QAAQY,IAAI,IAA2B7C,OAAO8C,KAAK,CAAC;YAC7D,GACAjC;YAGF,OAAO;gBAACgB,kBAAkBW,OAAOV,eAAe;gBAAEiB,QAAQP,OAAOO,MAAM;YAAA;QACzE;IACF;AACF"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { WorkflowsModuleClient } from '@shipfox/api-workflows-dto/inter-module';
|
|
1
2
|
import { type RouteGroup } from '@shipfox/node-fastify';
|
|
2
|
-
export declare
|
|
3
|
+
export declare function createLogsRoutes(workflows: WorkflowsModuleClient): RouteGroup[];
|
|
3
4
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/presentation/routes/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAsB,KAAK,UAAU,EAAC,MAAM,uBAAuB,CAAC;AAQ3E,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/presentation/routes/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAC,qBAAqB,EAAC,MAAM,yCAAyC,CAAC;AACnF,OAAO,EAAsB,KAAK,UAAU,EAAC,MAAM,uBAAuB,CAAC;AAQ3E,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,qBAAqB,GAAG,UAAU,EAAE,CAmB/E"}
|
|
@@ -1,32 +1,34 @@
|
|
|
1
1
|
import { AUTH_LEASED_JOB, AUTH_USER } from '@shipfox/api-auth-context';
|
|
2
2
|
import { createRawBodyPlugin } from '@shipfox/node-fastify';
|
|
3
3
|
import { config } from '#config.js';
|
|
4
|
-
import {
|
|
4
|
+
import { createAppendLogsRoute } from './append-logs.js';
|
|
5
5
|
import { readLogsRoute } from './read-logs.js';
|
|
6
6
|
// Keep the lease-authed append in its own Fastify scope so the raw NDJSON parser does not
|
|
7
7
|
// disturb the JSON read route (or workflow routes). The body limit also bounds one-append
|
|
8
8
|
// budget overshoot. The read route is session-authed and workspace-scoped via the row.
|
|
9
|
-
export
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
9
|
+
export function createLogsRoutes(workflows) {
|
|
10
|
+
return [
|
|
11
|
+
{
|
|
12
|
+
prefix: '/runs/jobs/current',
|
|
13
|
+
auth: AUTH_LEASED_JOB,
|
|
14
|
+
plugins: [
|
|
15
|
+
createRawBodyPlugin({
|
|
16
|
+
contentType: 'application/x-ndjson',
|
|
17
|
+
bodyLimit: config.LOG_APPEND_BODY_LIMIT_BYTES
|
|
18
|
+
})
|
|
19
|
+
],
|
|
20
|
+
routes: [
|
|
21
|
+
createAppendLogsRoute(workflows)
|
|
22
|
+
]
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
prefix: '/steps',
|
|
26
|
+
auth: AUTH_USER,
|
|
27
|
+
routes: [
|
|
28
|
+
readLogsRoute
|
|
29
|
+
]
|
|
30
|
+
}
|
|
31
|
+
];
|
|
32
|
+
}
|
|
31
33
|
|
|
32
34
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/presentation/routes/index.ts"],"sourcesContent":["import {AUTH_LEASED_JOB, AUTH_USER} from '@shipfox/api-auth-context';\nimport {createRawBodyPlugin, type RouteGroup} from '@shipfox/node-fastify';\nimport {config} from '#config.js';\nimport {
|
|
1
|
+
{"version":3,"sources":["../../../src/presentation/routes/index.ts"],"sourcesContent":["import {AUTH_LEASED_JOB, AUTH_USER} from '@shipfox/api-auth-context';\nimport type {WorkflowsModuleClient} from '@shipfox/api-workflows-dto/inter-module';\nimport {createRawBodyPlugin, type RouteGroup} from '@shipfox/node-fastify';\nimport {config} from '#config.js';\nimport {createAppendLogsRoute} from './append-logs.js';\nimport {readLogsRoute} from './read-logs.js';\n\n// Keep the lease-authed append in its own Fastify scope so the raw NDJSON parser does not\n// disturb the JSON read route (or workflow routes). The body limit also bounds one-append\n// budget overshoot. The read route is session-authed and workspace-scoped via the row.\nexport function createLogsRoutes(workflows: WorkflowsModuleClient): RouteGroup[] {\n return [\n {\n prefix: '/runs/jobs/current',\n auth: AUTH_LEASED_JOB,\n plugins: [\n createRawBodyPlugin({\n contentType: 'application/x-ndjson',\n bodyLimit: config.LOG_APPEND_BODY_LIMIT_BYTES,\n }),\n ],\n routes: [createAppendLogsRoute(workflows)],\n },\n {\n prefix: '/steps',\n auth: AUTH_USER,\n routes: [readLogsRoute],\n },\n ];\n}\n"],"names":["AUTH_LEASED_JOB","AUTH_USER","createRawBodyPlugin","config","createAppendLogsRoute","readLogsRoute","createLogsRoutes","workflows","prefix","auth","plugins","contentType","bodyLimit","LOG_APPEND_BODY_LIMIT_BYTES","routes"],"mappings":"AAAA,SAAQA,eAAe,EAAEC,SAAS,QAAO,4BAA4B;AAErE,SAAQC,mBAAmB,QAAwB,wBAAwB;AAC3E,SAAQC,MAAM,QAAO,aAAa;AAClC,SAAQC,qBAAqB,QAAO,mBAAmB;AACvD,SAAQC,aAAa,QAAO,iBAAiB;AAE7C,0FAA0F;AAC1F,0FAA0F;AAC1F,uFAAuF;AACvF,OAAO,SAASC,iBAAiBC,SAAgC;IAC/D,OAAO;QACL;YACEC,QAAQ;YACRC,MAAMT;YACNU,SAAS;gBACPR,oBAAoB;oBAClBS,aAAa;oBACbC,WAAWT,OAAOU,2BAA2B;gBAC/C;aACD;YACDC,QAAQ;gBAACV,sBAAsBG;aAAW;QAC5C;QACA;YACEC,QAAQ;YACRC,MAAMR;YACNa,QAAQ;gBAACT;aAAc;QACzB;KACD;AACH"}
|