@shipfox/annotations 12.0.0 → 12.3.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.
@@ -1,2 +1,2 @@
1
1
  $ shipfox-swc
2
- Successfully compiled: 17 files with swc (226.52ms)
2
+ Successfully compiled: 18 files with swc (421.16ms)
package/CHANGELOG.md CHANGED
@@ -1,5 +1,24 @@
1
1
  # @shipfox/annotations
2
2
 
3
+ ## 12.3.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 4b0731e: Adds workflow troubleshooting details, evaluation traces, failure annotations, runner context, step output metadata, and lazy paginated annotation summaries.
8
+
9
+ ### Patch Changes
10
+
11
+ - Updated dependencies [4b0731e]
12
+ - @shipfox/annotations-dto@12.3.0
13
+
14
+ ## 12.2.0
15
+
16
+ ### Patch Changes
17
+
18
+ - @shipfox/node-fastify@0.4.2
19
+ - @shipfox/node-module@1.0.6
20
+ - @shipfox/api-auth-context@12.2.0
21
+
3
22
  ## 12.0.0
4
23
 
5
24
  ### Patch Changes
@@ -1 +1 @@
1
- {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/core/errors.ts"],"names":[],"mappings":"AAAA,qBAAa,2BAA4B,SAAQ,KAAK;aACxB,QAAQ,EAAE,MAAM;gBAAhB,QAAQ,EAAE,MAAM;CAI7C;AAED,qBAAa,iCAAkC,SAAQ,KAAK;aAC9B,cAAc,EAAE,MAAM;gBAAtB,cAAc,EAAE,MAAM;CAInD;AAED,qBAAa,sCAAuC,SAAQ,KAAK;aACnC,QAAQ,EAAE,MAAM;gBAAhB,QAAQ,EAAE,MAAM;CAI7C"}
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/core/errors.ts"],"names":[],"mappings":"AAAA,qBAAa,2BAA4B,SAAQ,KAAK;aACxB,QAAQ,EAAE,MAAM;IAA5C,YAA4B,QAAQ,EAAE,MAAM,EAG3C;CACF;AAED,qBAAa,iCAAkC,SAAQ,KAAK;aAC9B,cAAc,EAAE,MAAM;IAAlD,YAA4B,cAAc,EAAE,MAAM,EAGjD;CACF;AAED,qBAAa,sCAAuC,SAAQ,KAAK;aACnC,QAAQ,EAAE,MAAM;IAA5C,YAA4B,QAAQ,EAAE,MAAM,EAG3C;CACF"}
@@ -21,6 +21,26 @@ export interface ListAnnotationsForRunAttemptResult {
21
21
  } | null;
22
22
  }
23
23
  export declare function listAnnotationsForRunAttempt(params: ListAnnotationsForRunAttemptParams): Promise<ListAnnotationsForRunAttemptResult>;
24
+ export interface AnnotationSummary {
25
+ total: number;
26
+ error: number;
27
+ warning: number;
28
+ info: number;
29
+ success: number;
30
+ stepCounts: Array<{
31
+ originStepId: string;
32
+ originStepAttempt: number;
33
+ total: number;
34
+ }>;
35
+ }
36
+ export interface SummarizeAnnotationsForRunAttemptParams {
37
+ workflowRunId: string;
38
+ workflowRunAttempt: number;
39
+ workspaceIds: readonly string[];
40
+ jobExecutionId?: string | undefined;
41
+ }
42
+ /** Count annotation styles without reading any annotation bodies. */
43
+ export declare function summarizeAnnotationsForRunAttempt(params: SummarizeAnnotationsForRunAttemptParams): Promise<AnnotationSummary>;
24
44
  export interface StoredAnnotation {
25
45
  id: string;
26
46
  context: string;
@@ -1 +1 @@
1
- {"version":3,"file":"annotations.d.ts","sourceRoot":"","sources":["../../src/db/annotations.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,KAAK,kBAAkB,EAA6B,MAAM,0BAA0B,CAAC;AAE7F,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,8BAA8B,CAAC;AAI7D,eAAO,MAAM,8BAA8B,MAA6B,CAAC;AAEzE,MAAM,WAAW,kCAAkC;IACjD,aAAa,EAAE,MAAM,CAAC;IACtB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,YAAY,EAAE,SAAS,MAAM,EAAE,CAAC;IAChC,cAAc,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACpC,KAAK,CAAC,EAAE;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAA;KAAC,GAAG,SAAS,CAAC;IACnD,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC5B;AAED,MAAM,WAAW,kCAAkC;IACjD,WAAW,EAAE,UAAU,EAAE,CAAC;IAC1B,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,EAAE;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAA;KAAC,GAAG,IAAI,CAAC;CACnD;AAED,wBAAsB,4BAA4B,CAChD,MAAM,EAAE,kCAAkC,GACzC,OAAO,CAAC,kCAAkC,CAAC,CAoC7C;AAED,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,kBAAkB,CAAC;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,sBAAsB;IACrC,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;IACtB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,oBAAoB,EAAE,MAAM,CAAC;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,cAAc,EAAE,MAAM,CAAC;IACvB,YAAY,EAAE,MAAM,CAAC;IACrB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,kBAAkB,CAAC;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,sBAAsB;IACrC,EAAE,EAAE,MAAM,CAAC;IACX,YAAY,EAAE,MAAM,CAAC;IACrB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,KAAK,EAAE,kBAAkB,CAAC;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,yBAAyB;IACxC,sBAAsB,CAAC,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC,CAAC;IACvF,gBAAgB,CAAC,cAAc,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACzE,gBAAgB,CAAC,MAAM,EAAE,sBAAsB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAAC;IAC5E,gBAAgB,CAAC,MAAM,EAAE,sBAAsB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAAC;CAC7E;AAED,wBAAgB,kBAAkB,CAAC,CAAC,EAClC,cAAc,EAAE,MAAM,EACtB,IAAI,EAAE,CAAC,IAAI,EAAE,yBAAyB,KAAK,OAAO,CAAC,CAAC,CAAC,GACpD,OAAO,CAAC,CAAC,CAAC,CAuEZ"}
1
+ {"version":3,"file":"annotations.d.ts","sourceRoot":"","sources":["../../src/db/annotations.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  import { READ_ANNOTATIONS_MAX_LIMIT } from '@shipfox/annotations-dto';
2
- import { and, asc, eq, gt, inArray, or, sql } from 'drizzle-orm';
2
+ import { and, asc, count, eq, gt, inArray, or, sql } from 'drizzle-orm';
3
3
  import { db } from './db.js';
4
4
  import { annotations, toAnnotation } from './schema/annotations.js';
5
5
  export const DEFAULT_ANNOTATIONS_READ_LIMIT = READ_ANNOTATIONS_MAX_LIMIT;
@@ -36,6 +36,56 @@ export async function listAnnotationsForRunAttempt(params) {
36
36
  } : null
37
37
  };
38
38
  }
39
+ /** Count annotation styles without reading any annotation bodies. */ export async function summarizeAnnotationsForRunAttempt(params) {
40
+ const summary = {
41
+ total: 0,
42
+ error: 0,
43
+ warning: 0,
44
+ info: 0,
45
+ success: 0,
46
+ stepCounts: []
47
+ };
48
+ if (params.workspaceIds.length === 0) return summary;
49
+ const conditions = [
50
+ eq(annotations.workflowRunId, params.workflowRunId),
51
+ eq(annotations.workflowRunAttempt, params.workflowRunAttempt),
52
+ inArray(annotations.workspaceId, [
53
+ ...params.workspaceIds
54
+ ])
55
+ ];
56
+ if (params.jobExecutionId) {
57
+ conditions.push(eq(annotations.jobExecutionId, params.jobExecutionId));
58
+ }
59
+ const { rows, stepRows } = await db().transaction(async (tx)=>{
60
+ // Both aggregates must observe the same committed state. READ COMMITTED would allow a
61
+ // concurrent annotation write between these selects, producing contradictory totals.
62
+ await tx.execute(sql`set transaction isolation level repeatable read, read only`);
63
+ const rows = await tx.select({
64
+ style: annotations.style,
65
+ count: count()
66
+ }).from(annotations).where(and(...conditions)).groupBy(annotations.style);
67
+ const stepRows = await tx.select({
68
+ originStepId: annotations.originStepId,
69
+ originStepAttempt: annotations.originStepAttempt,
70
+ total: count()
71
+ }).from(annotations).where(and(...conditions)).groupBy(annotations.originStepId, annotations.originStepAttempt).orderBy(asc(annotations.originStepId), asc(annotations.originStepAttempt));
72
+ return {
73
+ rows,
74
+ stepRows
75
+ };
76
+ });
77
+ for (const row of rows){
78
+ const value = Number(row.count);
79
+ summary.total += value;
80
+ if (row.style !== 'default') summary[row.style] += value;
81
+ }
82
+ summary.stepCounts = stepRows.map((row)=>({
83
+ originStepId: row.originStepId,
84
+ originStepAttempt: row.originStepAttempt,
85
+ total: Number(row.total)
86
+ }));
87
+ return summary;
88
+ }
39
89
  export function withAnnotationLock(jobExecutionId, work) {
40
90
  return db().transaction(async (tx)=>{
41
91
  await tx.execute(sql`select pg_advisory_xact_lock(hashtext(${jobExecutionId}))`);
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/db/annotations.ts"],"sourcesContent":["import {type AnnotationStyleDto, READ_ANNOTATIONS_MAX_LIMIT} from '@shipfox/annotations-dto';\nimport {and, asc, eq, gt, inArray, or, type SQL, sql} from 'drizzle-orm';\nimport type {Annotation} from '#core/entities/annotation.js';\nimport {db} from './db.js';\nimport {annotations, toAnnotation} from './schema/annotations.js';\n\nexport const DEFAULT_ANNOTATIONS_READ_LIMIT = READ_ANNOTATIONS_MAX_LIMIT;\n\nexport interface ListAnnotationsForRunAttemptParams {\n workflowRunId: string;\n workflowRunAttempt: number;\n workspaceIds: readonly string[];\n jobExecutionId?: string | undefined;\n after?: {sequence: number; id: string} | undefined;\n limit?: number | undefined;\n}\n\nexport interface ListAnnotationsForRunAttemptResult {\n annotations: Annotation[];\n hasMore: boolean;\n nextCursor: {sequence: number; id: string} | null;\n}\n\nexport async function listAnnotationsForRunAttempt(\n params: ListAnnotationsForRunAttemptParams,\n): Promise<ListAnnotationsForRunAttemptResult> {\n if (params.workspaceIds.length === 0) return {annotations: [], hasMore: false, nextCursor: null};\n\n const limit = params.limit ?? DEFAULT_ANNOTATIONS_READ_LIMIT;\n\n const conditions: SQL[] = [\n eq(annotations.workflowRunId, params.workflowRunId),\n eq(annotations.workflowRunAttempt, params.workflowRunAttempt),\n inArray(annotations.workspaceId, [...params.workspaceIds]),\n ];\n if (params.jobExecutionId) {\n conditions.push(eq(annotations.jobExecutionId, params.jobExecutionId));\n }\n if (params.after) {\n const cursorCondition = or(\n gt(annotations.sequence, params.after.sequence),\n and(eq(annotations.sequence, params.after.sequence), gt(annotations.id, params.after.id)),\n );\n if (cursorCondition) conditions.push(cursorCondition);\n }\n\n const rows = await db()\n .select()\n .from(annotations)\n .where(and(...conditions))\n .orderBy(asc(annotations.sequence), asc(annotations.id))\n .limit(limit + 1);\n\n const pageRows = rows.slice(0, limit);\n const last = pageRows.at(-1);\n\n return {\n annotations: pageRows.map(toAnnotation),\n hasMore: rows.length > limit,\n nextCursor: rows.length > limit && last ? {sequence: last.sequence, id: last.id} : null,\n };\n}\n\nexport interface StoredAnnotation {\n id: string;\n context: string;\n style: AnnotationStyleDto;\n body: string;\n bodyBytes: number;\n sequence: number;\n}\n\nexport interface CreateAnnotationParams {\n workspaceId: string;\n projectId: string;\n workflowRunId: string;\n workflowRunAttempt: number;\n workflowRunAttemptId: string;\n jobId: string;\n jobExecutionId: string;\n originStepId: string;\n originStepAttempt: number;\n context: string;\n style: AnnotationStyleDto;\n body: string;\n bodyBytes: number;\n sequence: number;\n}\n\nexport interface UpdateAnnotationParams {\n id: string;\n originStepId: string;\n originStepAttempt: number;\n style: AnnotationStyleDto;\n body: string;\n bodyBytes: number;\n}\n\nexport interface AnnotationWriteRepository {\n loadCurrentAnnotations(jobExecutionId: string): Promise<Map<string, StoredAnnotation>>;\n removeAnnotation(jobExecutionId: string, context: string): Promise<void>;\n createAnnotation(params: CreateAnnotationParams): Promise<StoredAnnotation>;\n updateAnnotation(params: UpdateAnnotationParams): Promise<StoredAnnotation>;\n}\n\nexport function withAnnotationLock<T>(\n jobExecutionId: string,\n work: (repo: AnnotationWriteRepository) => Promise<T>,\n): Promise<T> {\n return db().transaction(async (tx) => {\n await tx.execute(sql`select pg_advisory_xact_lock(hashtext(${jobExecutionId}))`);\n\n const repo: AnnotationWriteRepository = {\n loadCurrentAnnotations: async (lockedJobExecutionId) => {\n const rows = await tx\n .select({\n id: annotations.id,\n context: annotations.context,\n style: annotations.style,\n body: annotations.body,\n bodyBytes: annotations.bodyBytes,\n sequence: annotations.sequence,\n })\n .from(annotations)\n .where(eq(annotations.jobExecutionId, lockedJobExecutionId));\n\n return new Map(rows.map((row) => [row.context, row]));\n },\n removeAnnotation: async (lockedJobExecutionId, context) => {\n await tx\n .delete(annotations)\n .where(\n and(\n eq(annotations.jobExecutionId, lockedJobExecutionId),\n eq(annotations.context, context),\n ),\n );\n },\n createAnnotation: async (params) => {\n const [row] = await tx.insert(annotations).values(params).returning({\n id: annotations.id,\n context: annotations.context,\n style: annotations.style,\n body: annotations.body,\n bodyBytes: annotations.bodyBytes,\n sequence: annotations.sequence,\n });\n\n if (!row) throw new Error('createAnnotation: insert returned no row');\n return row;\n },\n updateAnnotation: async (params) => {\n const [row] = await tx\n .update(annotations)\n .set({\n originStepId: params.originStepId,\n originStepAttempt: params.originStepAttempt,\n style: params.style,\n body: params.body,\n bodyBytes: params.bodyBytes,\n updatedAt: new Date(),\n })\n .where(eq(annotations.id, params.id))\n .returning({\n id: annotations.id,\n context: annotations.context,\n style: annotations.style,\n body: annotations.body,\n bodyBytes: annotations.bodyBytes,\n sequence: annotations.sequence,\n });\n\n if (!row) throw new Error('updateAnnotation: update returned no row');\n return row;\n },\n };\n\n return await work(repo);\n });\n}\n"],"names":["READ_ANNOTATIONS_MAX_LIMIT","and","asc","eq","gt","inArray","or","sql","db","annotations","toAnnotation","DEFAULT_ANNOTATIONS_READ_LIMIT","listAnnotationsForRunAttempt","params","workspaceIds","length","hasMore","nextCursor","limit","conditions","workflowRunId","workflowRunAttempt","workspaceId","jobExecutionId","push","after","cursorCondition","sequence","id","rows","select","from","where","orderBy","pageRows","slice","last","at","map","withAnnotationLock","work","transaction","tx","execute","repo","loadCurrentAnnotations","lockedJobExecutionId","context","style","body","bodyBytes","Map","row","removeAnnotation","delete","createAnnotation","insert","values","returning","Error","updateAnnotation","update","set","originStepId","originStepAttempt","updatedAt","Date"],"mappings":"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"}
1
+ {"version":3,"sources":["../../src/db/annotations.ts"],"sourcesContent":["import {type AnnotationStyleDto, READ_ANNOTATIONS_MAX_LIMIT} from '@shipfox/annotations-dto';\nimport {and, asc, count, eq, gt, inArray, or, type SQL, sql} from 'drizzle-orm';\nimport type {Annotation} from '#core/entities/annotation.js';\nimport {db} from './db.js';\nimport {annotations, toAnnotation} from './schema/annotations.js';\n\nexport const DEFAULT_ANNOTATIONS_READ_LIMIT = READ_ANNOTATIONS_MAX_LIMIT;\n\nexport interface ListAnnotationsForRunAttemptParams {\n workflowRunId: string;\n workflowRunAttempt: number;\n workspaceIds: readonly string[];\n jobExecutionId?: string | undefined;\n after?: {sequence: number; id: string} | undefined;\n limit?: number | undefined;\n}\n\nexport interface ListAnnotationsForRunAttemptResult {\n annotations: Annotation[];\n hasMore: boolean;\n nextCursor: {sequence: number; id: string} | null;\n}\n\nexport async function listAnnotationsForRunAttempt(\n params: ListAnnotationsForRunAttemptParams,\n): Promise<ListAnnotationsForRunAttemptResult> {\n if (params.workspaceIds.length === 0) return {annotations: [], hasMore: false, nextCursor: null};\n\n const limit = params.limit ?? DEFAULT_ANNOTATIONS_READ_LIMIT;\n\n const conditions: SQL[] = [\n eq(annotations.workflowRunId, params.workflowRunId),\n eq(annotations.workflowRunAttempt, params.workflowRunAttempt),\n inArray(annotations.workspaceId, [...params.workspaceIds]),\n ];\n if (params.jobExecutionId) {\n conditions.push(eq(annotations.jobExecutionId, params.jobExecutionId));\n }\n if (params.after) {\n const cursorCondition = or(\n gt(annotations.sequence, params.after.sequence),\n and(eq(annotations.sequence, params.after.sequence), gt(annotations.id, params.after.id)),\n );\n if (cursorCondition) conditions.push(cursorCondition);\n }\n\n const rows = await db()\n .select()\n .from(annotations)\n .where(and(...conditions))\n .orderBy(asc(annotations.sequence), asc(annotations.id))\n .limit(limit + 1);\n\n const pageRows = rows.slice(0, limit);\n const last = pageRows.at(-1);\n\n return {\n annotations: pageRows.map(toAnnotation),\n hasMore: rows.length > limit,\n nextCursor: rows.length > limit && last ? {sequence: last.sequence, id: last.id} : null,\n };\n}\n\nexport interface AnnotationSummary {\n total: number;\n error: number;\n warning: number;\n info: number;\n success: number;\n stepCounts: Array<{\n originStepId: string;\n originStepAttempt: number;\n total: number;\n }>;\n}\n\nexport interface SummarizeAnnotationsForRunAttemptParams {\n workflowRunId: string;\n workflowRunAttempt: number;\n workspaceIds: readonly string[];\n jobExecutionId?: string | undefined;\n}\n\n/** Count annotation styles without reading any annotation bodies. */\nexport async function summarizeAnnotationsForRunAttempt(\n params: SummarizeAnnotationsForRunAttemptParams,\n): Promise<AnnotationSummary> {\n const summary: AnnotationSummary = {\n total: 0,\n error: 0,\n warning: 0,\n info: 0,\n success: 0,\n stepCounts: [],\n };\n if (params.workspaceIds.length === 0) return summary;\n\n const conditions: SQL[] = [\n eq(annotations.workflowRunId, params.workflowRunId),\n eq(annotations.workflowRunAttempt, params.workflowRunAttempt),\n inArray(annotations.workspaceId, [...params.workspaceIds]),\n ];\n if (params.jobExecutionId) {\n conditions.push(eq(annotations.jobExecutionId, params.jobExecutionId));\n }\n\n const {rows, stepRows} = await db().transaction(async (tx) => {\n // Both aggregates must observe the same committed state. READ COMMITTED would allow a\n // concurrent annotation write between these selects, producing contradictory totals.\n await tx.execute(sql`set transaction isolation level repeatable read, read only`);\n\n const rows = await tx\n .select({style: annotations.style, count: count()})\n .from(annotations)\n .where(and(...conditions))\n .groupBy(annotations.style);\n\n const stepRows = await tx\n .select({\n originStepId: annotations.originStepId,\n originStepAttempt: annotations.originStepAttempt,\n total: count(),\n })\n .from(annotations)\n .where(and(...conditions))\n .groupBy(annotations.originStepId, annotations.originStepAttempt)\n .orderBy(asc(annotations.originStepId), asc(annotations.originStepAttempt));\n\n return {rows, stepRows};\n });\n\n for (const row of rows) {\n const value = Number(row.count);\n summary.total += value;\n if (row.style !== 'default') summary[row.style] += value;\n }\n\n summary.stepCounts = stepRows.map((row) => ({\n originStepId: row.originStepId,\n originStepAttempt: row.originStepAttempt,\n total: Number(row.total),\n }));\n\n return summary;\n}\n\nexport interface StoredAnnotation {\n id: string;\n context: string;\n style: AnnotationStyleDto;\n body: string;\n bodyBytes: number;\n sequence: number;\n}\n\nexport interface CreateAnnotationParams {\n workspaceId: string;\n projectId: string;\n workflowRunId: string;\n workflowRunAttempt: number;\n workflowRunAttemptId: string;\n jobId: string;\n jobExecutionId: string;\n originStepId: string;\n originStepAttempt: number;\n context: string;\n style: AnnotationStyleDto;\n body: string;\n bodyBytes: number;\n sequence: number;\n}\n\nexport interface UpdateAnnotationParams {\n id: string;\n originStepId: string;\n originStepAttempt: number;\n style: AnnotationStyleDto;\n body: string;\n bodyBytes: number;\n}\n\nexport interface AnnotationWriteRepository {\n loadCurrentAnnotations(jobExecutionId: string): Promise<Map<string, StoredAnnotation>>;\n removeAnnotation(jobExecutionId: string, context: string): Promise<void>;\n createAnnotation(params: CreateAnnotationParams): Promise<StoredAnnotation>;\n updateAnnotation(params: UpdateAnnotationParams): Promise<StoredAnnotation>;\n}\n\nexport function withAnnotationLock<T>(\n jobExecutionId: string,\n work: (repo: AnnotationWriteRepository) => Promise<T>,\n): Promise<T> {\n return db().transaction(async (tx) => {\n await tx.execute(sql`select pg_advisory_xact_lock(hashtext(${jobExecutionId}))`);\n\n const repo: AnnotationWriteRepository = {\n loadCurrentAnnotations: async (lockedJobExecutionId) => {\n const rows = await tx\n .select({\n id: annotations.id,\n context: annotations.context,\n style: annotations.style,\n body: annotations.body,\n bodyBytes: annotations.bodyBytes,\n sequence: annotations.sequence,\n })\n .from(annotations)\n .where(eq(annotations.jobExecutionId, lockedJobExecutionId));\n\n return new Map(rows.map((row) => [row.context, row]));\n },\n removeAnnotation: async (lockedJobExecutionId, context) => {\n await tx\n .delete(annotations)\n .where(\n and(\n eq(annotations.jobExecutionId, lockedJobExecutionId),\n eq(annotations.context, context),\n ),\n );\n },\n createAnnotation: async (params) => {\n const [row] = await tx.insert(annotations).values(params).returning({\n id: annotations.id,\n context: annotations.context,\n style: annotations.style,\n body: annotations.body,\n bodyBytes: annotations.bodyBytes,\n sequence: annotations.sequence,\n });\n\n if (!row) throw new Error('createAnnotation: insert returned no row');\n return row;\n },\n updateAnnotation: async (params) => {\n const [row] = await tx\n .update(annotations)\n .set({\n originStepId: params.originStepId,\n originStepAttempt: params.originStepAttempt,\n style: params.style,\n body: params.body,\n bodyBytes: params.bodyBytes,\n updatedAt: new Date(),\n })\n .where(eq(annotations.id, params.id))\n .returning({\n id: annotations.id,\n context: annotations.context,\n style: annotations.style,\n body: annotations.body,\n bodyBytes: annotations.bodyBytes,\n sequence: annotations.sequence,\n });\n\n if (!row) throw new Error('updateAnnotation: update returned no row');\n return row;\n },\n };\n\n return await work(repo);\n });\n}\n"],"names":["READ_ANNOTATIONS_MAX_LIMIT","and","asc","count","eq","gt","inArray","or","sql","db","annotations","toAnnotation","DEFAULT_ANNOTATIONS_READ_LIMIT","listAnnotationsForRunAttempt","params","workspaceIds","length","hasMore","nextCursor","limit","conditions","workflowRunId","workflowRunAttempt","workspaceId","jobExecutionId","push","after","cursorCondition","sequence","id","rows","select","from","where","orderBy","pageRows","slice","last","at","map","summarizeAnnotationsForRunAttempt","summary","total","error","warning","info","success","stepCounts","stepRows","transaction","tx","execute","style","groupBy","originStepId","originStepAttempt","row","value","Number","withAnnotationLock","work","repo","loadCurrentAnnotations","lockedJobExecutionId","context","body","bodyBytes","Map","removeAnnotation","delete","createAnnotation","insert","values","returning","Error","updateAnnotation","update","set","updatedAt","Date"],"mappings":"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"}
package/dist/db/db.d.ts CHANGED
@@ -196,7 +196,7 @@ export declare const schema: {
196
196
  tableName: "annotations";
197
197
  dataType: "string";
198
198
  columnType: "PgEnumColumn";
199
- data: "default" | "info" | "success" | "warning" | "error";
199
+ data: "default" | "error" | "info" | "success" | "warning";
200
200
  driverParam: string;
201
201
  notNull: true;
202
202
  hasDefault: true;
@@ -294,7 +294,7 @@ export declare const schema: {
294
294
  generated: undefined;
295
295
  }, {}, {}>;
296
296
  };
297
- dialect: "pg";
297
+ dialect: 'pg';
298
298
  }>;
299
299
  };
300
300
  export type Database = NodePgDatabase<typeof schema>;
@@ -1,4 +1,4 @@
1
1
  export declare const migrationsPath: string;
2
- export type { ListAnnotationsForRunAttemptParams } from './annotations.js';
3
- export { DEFAULT_ANNOTATIONS_READ_LIMIT, listAnnotationsForRunAttempt } from './annotations.js';
2
+ export type { AnnotationSummary, ListAnnotationsForRunAttemptParams, SummarizeAnnotationsForRunAttemptParams, } from './annotations.js';
3
+ export { DEFAULT_ANNOTATIONS_READ_LIMIT, listAnnotationsForRunAttempt, summarizeAnnotationsForRunAttempt, } from './annotations.js';
4
4
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/db/index.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,cAAc,QAAoE,CAAC;AAEhG,YAAY,EAAC,kCAAkC,EAAC,MAAM,kBAAkB,CAAC;AACzE,OAAO,EAAC,8BAA8B,EAAE,4BAA4B,EAAC,MAAM,kBAAkB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/db/index.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,cAAc,QAAoE,CAAC;AAEhG,YAAY,EACV,iBAAiB,EACjB,kCAAkC,EAClC,uCAAuC,GACxC,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EACL,8BAA8B,EAC9B,4BAA4B,EAC5B,iCAAiC,GAClC,MAAM,kBAAkB,CAAC"}
package/dist/db/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { dirname, resolve } from 'node:path';
2
2
  import { fileURLToPath } from 'node:url';
3
3
  export const migrationsPath = resolve(dirname(fileURLToPath(import.meta.url)), '../../drizzle');
4
- export { DEFAULT_ANNOTATIONS_READ_LIMIT, listAnnotationsForRunAttempt } from './annotations.js';
4
+ export { DEFAULT_ANNOTATIONS_READ_LIMIT, listAnnotationsForRunAttempt, summarizeAnnotationsForRunAttempt } from './annotations.js';
5
5
 
6
6
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/db/index.ts"],"sourcesContent":["import {dirname, resolve} from 'node:path';\nimport {fileURLToPath} from 'node:url';\n\nexport const migrationsPath = resolve(dirname(fileURLToPath(import.meta.url)), '../../drizzle');\n\nexport type {ListAnnotationsForRunAttemptParams} from './annotations.js';\nexport {DEFAULT_ANNOTATIONS_READ_LIMIT, listAnnotationsForRunAttempt} from './annotations.js';\n"],"names":["dirname","resolve","fileURLToPath","migrationsPath","url","DEFAULT_ANNOTATIONS_READ_LIMIT","listAnnotationsForRunAttempt"],"mappings":"AAAA,SAAQA,OAAO,EAAEC,OAAO,QAAO,YAAY;AAC3C,SAAQC,aAAa,QAAO,WAAW;AAEvC,OAAO,MAAMC,iBAAiBF,QAAQD,QAAQE,cAAc,YAAYE,GAAG,IAAI,iBAAiB;AAGhG,SAAQC,8BAA8B,EAAEC,4BAA4B,QAAO,mBAAmB"}
1
+ {"version":3,"sources":["../../src/db/index.ts"],"sourcesContent":["import {dirname, resolve} from 'node:path';\nimport {fileURLToPath} from 'node:url';\n\nexport const migrationsPath = resolve(dirname(fileURLToPath(import.meta.url)), '../../drizzle');\n\nexport type {\n AnnotationSummary,\n ListAnnotationsForRunAttemptParams,\n SummarizeAnnotationsForRunAttemptParams,\n} from './annotations.js';\nexport {\n DEFAULT_ANNOTATIONS_READ_LIMIT,\n listAnnotationsForRunAttempt,\n summarizeAnnotationsForRunAttempt,\n} from './annotations.js';\n"],"names":["dirname","resolve","fileURLToPath","migrationsPath","url","DEFAULT_ANNOTATIONS_READ_LIMIT","listAnnotationsForRunAttempt","summarizeAnnotationsForRunAttempt"],"mappings":"AAAA,SAAQA,OAAO,EAAEC,OAAO,QAAO,YAAY;AAC3C,SAAQC,aAAa,QAAO,WAAW;AAEvC,OAAO,MAAMC,iBAAiBF,QAAQD,QAAQE,cAAc,YAAYE,GAAG,IAAI,iBAAiB;AAOhG,SACEC,8BAA8B,EAC9BC,4BAA4B,EAC5BC,iCAAiC,QAC5B,mBAAmB"}
@@ -196,7 +196,7 @@ export declare const annotations: import("drizzle-orm/pg-core").PgTableWithColum
196
196
  tableName: "annotations";
197
197
  dataType: "string";
198
198
  columnType: "PgEnumColumn";
199
- data: "default" | "info" | "success" | "warning" | "error";
199
+ data: "default" | "error" | "info" | "success" | "warning";
200
200
  driverParam: string;
201
201
  notNull: true;
202
202
  hasDefault: true;
@@ -294,7 +294,7 @@ export declare const annotations: import("drizzle-orm/pg-core").PgTableWithColum
294
294
  generated: undefined;
295
295
  }, {}, {}>;
296
296
  };
297
- dialect: "pg";
297
+ dialect: 'pg';
298
298
  }>;
299
299
  export type AnnotationDb = typeof annotations.$inferSelect;
300
300
  export type AnnotationCreateDb = typeof annotations.$inferInsert;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/presentation/routes/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,uBAAuB,CAAC;AAItD,eAAO,MAAM,iBAAiB,EAAE,UAAU,EAWzC,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/presentation/routes/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,uBAAuB,CAAC;AAKtD,eAAO,MAAM,iBAAiB,EAAE,UAAU,EAWzC,CAAC"}
@@ -1,4 +1,5 @@
1
1
  import { AUTH_LEASED_JOB, AUTH_USER } from '@shipfox/api-auth-context';
2
+ import { readAnnotationSummaryRoute } from './read-annotation-summary.js';
2
3
  import { readAnnotationsRoute } from './read-annotations.js';
3
4
  import { writeAnnotationsRoute } from './write-annotations.js';
4
5
  export const annotationsRoutes = [
@@ -6,6 +7,7 @@ export const annotationsRoutes = [
6
7
  prefix: '/annotations',
7
8
  auth: AUTH_USER,
8
9
  routes: [
10
+ readAnnotationSummaryRoute,
9
11
  readAnnotationsRoute
10
12
  ]
11
13
  },
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/presentation/routes/index.ts"],"sourcesContent":["import {AUTH_LEASED_JOB, AUTH_USER} from '@shipfox/api-auth-context';\nimport type {RouteGroup} from '@shipfox/node-fastify';\nimport {readAnnotationsRoute} from './read-annotations.js';\nimport {writeAnnotationsRoute} from './write-annotations.js';\n\nexport const annotationsRoutes: RouteGroup[] = [\n {\n prefix: '/annotations',\n auth: AUTH_USER,\n routes: [readAnnotationsRoute],\n },\n {\n prefix: '/runs/jobs/current',\n auth: AUTH_LEASED_JOB,\n routes: [writeAnnotationsRoute],\n },\n];\n"],"names":["AUTH_LEASED_JOB","AUTH_USER","readAnnotationsRoute","writeAnnotationsRoute","annotationsRoutes","prefix","auth","routes"],"mappings":"AAAA,SAAQA,eAAe,EAAEC,SAAS,QAAO,4BAA4B;AAErE,SAAQC,oBAAoB,QAAO,wBAAwB;AAC3D,SAAQC,qBAAqB,QAAO,yBAAyB;AAE7D,OAAO,MAAMC,oBAAkC;IAC7C;QACEC,QAAQ;QACRC,MAAML;QACNM,QAAQ;YAACL;SAAqB;IAChC;IACA;QACEG,QAAQ;QACRC,MAAMN;QACNO,QAAQ;YAACJ;SAAsB;IACjC;CACD,CAAC"}
1
+ {"version":3,"sources":["../../../src/presentation/routes/index.ts"],"sourcesContent":["import {AUTH_LEASED_JOB, AUTH_USER} from '@shipfox/api-auth-context';\nimport type {RouteGroup} from '@shipfox/node-fastify';\nimport {readAnnotationSummaryRoute} from './read-annotation-summary.js';\nimport {readAnnotationsRoute} from './read-annotations.js';\nimport {writeAnnotationsRoute} from './write-annotations.js';\n\nexport const annotationsRoutes: RouteGroup[] = [\n {\n prefix: '/annotations',\n auth: AUTH_USER,\n routes: [readAnnotationSummaryRoute, readAnnotationsRoute],\n },\n {\n prefix: '/runs/jobs/current',\n auth: AUTH_LEASED_JOB,\n routes: [writeAnnotationsRoute],\n },\n];\n"],"names":["AUTH_LEASED_JOB","AUTH_USER","readAnnotationSummaryRoute","readAnnotationsRoute","writeAnnotationsRoute","annotationsRoutes","prefix","auth","routes"],"mappings":"AAAA,SAAQA,eAAe,EAAEC,SAAS,QAAO,4BAA4B;AAErE,SAAQC,0BAA0B,QAAO,+BAA+B;AACxE,SAAQC,oBAAoB,QAAO,wBAAwB;AAC3D,SAAQC,qBAAqB,QAAO,yBAAyB;AAE7D,OAAO,MAAMC,oBAAkC;IAC7C;QACEC,QAAQ;QACRC,MAAMN;QACNO,QAAQ;YAACN;YAA4BC;SAAqB;IAC5D;IACA;QACEG,QAAQ;QACRC,MAAMP;QACNQ,QAAQ;YAACJ;SAAsB;IACjC;CACD,CAAC"}
@@ -0,0 +1,2 @@
1
+ export declare const readAnnotationSummaryRoute: import("@shipfox/node-fastify").RouteDefinition;
2
+ //# sourceMappingURL=read-annotation-summary.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"read-annotation-summary.d.ts","sourceRoot":"","sources":["../../../src/presentation/routes/read-annotation-summary.ts"],"names":[],"mappings":"AAQA,eAAO,MAAM,0BAA0B,iDAyCrC,CAAC"}
@@ -0,0 +1,43 @@
1
+ import { annotationSummaryResponseSchema, readAnnotationsQuerySchema } from '@shipfox/annotations-dto';
2
+ import { requireUserContext } from '@shipfox/api-auth-context';
3
+ import { defineRoute } from '@shipfox/node-fastify';
4
+ import { summarizeAnnotationsForRunAttempt } from '#db/index.js';
5
+ export const readAnnotationSummaryRoute = defineRoute({
6
+ method: 'GET',
7
+ path: '/summary',
8
+ description: 'Read annotation counts for a workflow run attempt.',
9
+ schema: {
10
+ querystring: readAnnotationsQuerySchema.omit({
11
+ cursor: true,
12
+ limit: true
13
+ }),
14
+ response: {
15
+ 200: annotationSummaryResponseSchema
16
+ }
17
+ },
18
+ handler: async (request)=>{
19
+ const user = requireUserContext(request);
20
+ const { workflow_run_id: workflowRunId, attempt, job_execution_id: jobExecutionId } = request.query;
21
+ const workspaceIds = user.memberships.filter((membership)=>membership.workspaceStatus === 'active').map((membership)=>membership.workspaceId);
22
+ const summary = await summarizeAnnotationsForRunAttempt({
23
+ workflowRunId,
24
+ workflowRunAttempt: attempt,
25
+ workspaceIds,
26
+ jobExecutionId
27
+ });
28
+ return {
29
+ total: summary.total,
30
+ error: summary.error,
31
+ warning: summary.warning,
32
+ info: summary.info,
33
+ success: summary.success,
34
+ step_counts: summary.stepCounts.map((step)=>({
35
+ origin_step_id: step.originStepId,
36
+ origin_step_attempt: step.originStepAttempt,
37
+ total: step.total
38
+ }))
39
+ };
40
+ }
41
+ });
42
+
43
+ //# sourceMappingURL=read-annotation-summary.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/presentation/routes/read-annotation-summary.ts"],"sourcesContent":["import {\n annotationSummaryResponseSchema,\n readAnnotationsQuerySchema,\n} from '@shipfox/annotations-dto';\nimport {requireUserContext} from '@shipfox/api-auth-context';\nimport {defineRoute} from '@shipfox/node-fastify';\nimport {summarizeAnnotationsForRunAttempt} from '#db/index.js';\n\nexport const readAnnotationSummaryRoute = defineRoute({\n method: 'GET',\n path: '/summary',\n description: 'Read annotation counts for a workflow run attempt.',\n schema: {\n querystring: readAnnotationsQuerySchema.omit({cursor: true, limit: true}),\n response: {\n 200: annotationSummaryResponseSchema,\n },\n },\n handler: async (request) => {\n const user = requireUserContext(request);\n const {\n workflow_run_id: workflowRunId,\n attempt,\n job_execution_id: jobExecutionId,\n } = request.query;\n const workspaceIds = user.memberships\n .filter((membership) => membership.workspaceStatus === 'active')\n .map((membership) => membership.workspaceId);\n\n const summary = await summarizeAnnotationsForRunAttempt({\n workflowRunId,\n workflowRunAttempt: attempt,\n workspaceIds,\n jobExecutionId,\n });\n\n return {\n total: summary.total,\n error: summary.error,\n warning: summary.warning,\n info: summary.info,\n success: summary.success,\n step_counts: summary.stepCounts.map((step) => ({\n origin_step_id: step.originStepId,\n origin_step_attempt: step.originStepAttempt,\n total: step.total,\n })),\n };\n },\n});\n"],"names":["annotationSummaryResponseSchema","readAnnotationsQuerySchema","requireUserContext","defineRoute","summarizeAnnotationsForRunAttempt","readAnnotationSummaryRoute","method","path","description","schema","querystring","omit","cursor","limit","response","handler","request","user","workflow_run_id","workflowRunId","attempt","job_execution_id","jobExecutionId","query","workspaceIds","memberships","filter","membership","workspaceStatus","map","workspaceId","summary","workflowRunAttempt","total","error","warning","info","success","step_counts","stepCounts","step","origin_step_id","originStepId","origin_step_attempt","originStepAttempt"],"mappings":"AAAA,SACEA,+BAA+B,EAC/BC,0BAA0B,QACrB,2BAA2B;AAClC,SAAQC,kBAAkB,QAAO,4BAA4B;AAC7D,SAAQC,WAAW,QAAO,wBAAwB;AAClD,SAAQC,iCAAiC,QAAO,eAAe;AAE/D,OAAO,MAAMC,6BAA6BF,YAAY;IACpDG,QAAQ;IACRC,MAAM;IACNC,aAAa;IACbC,QAAQ;QACNC,aAAaT,2BAA2BU,IAAI,CAAC;YAACC,QAAQ;YAAMC,OAAO;QAAI;QACvEC,UAAU;YACR,KAAKd;QACP;IACF;IACAe,SAAS,OAAOC;QACd,MAAMC,OAAOf,mBAAmBc;QAChC,MAAM,EACJE,iBAAiBC,aAAa,EAC9BC,OAAO,EACPC,kBAAkBC,cAAc,EACjC,GAAGN,QAAQO,KAAK;QACjB,MAAMC,eAAeP,KAAKQ,WAAW,CAClCC,MAAM,CAAC,CAACC,aAAeA,WAAWC,eAAe,KAAK,UACtDC,GAAG,CAAC,CAACF,aAAeA,WAAWG,WAAW;QAE7C,MAAMC,UAAU,MAAM3B,kCAAkC;YACtDe;YACAa,oBAAoBZ;YACpBI;YACAF;QACF;QAEA,OAAO;YACLW,OAAOF,QAAQE,KAAK;YACpBC,OAAOH,QAAQG,KAAK;YACpBC,SAASJ,QAAQI,OAAO;YACxBC,MAAML,QAAQK,IAAI;YAClBC,SAASN,QAAQM,OAAO;YACxBC,aAAaP,QAAQQ,UAAU,CAACV,GAAG,CAAC,CAACW,OAAU,CAAA;oBAC7CC,gBAAgBD,KAAKE,YAAY;oBACjCC,qBAAqBH,KAAKI,iBAAiB;oBAC3CX,OAAOO,KAAKP,KAAK;gBACnB,CAAA;QACF;IACF;AACF,GAAG"}