@shipfox/api-definitions 5.0.0 → 7.1.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.
Files changed (93) hide show
  1. package/.turbo/turbo-build.log +11 -13
  2. package/CHANGELOG.md +49 -0
  3. package/dist/core/entities/workflow-definition.d.ts +2 -5
  4. package/dist/core/entities/workflow-definition.d.ts.map +1 -1
  5. package/dist/core/entities/workflow-definition.js.map +1 -1
  6. package/dist/core/entities/workflow-model.d.ts +1 -137
  7. package/dist/core/entities/workflow-model.d.ts.map +1 -1
  8. package/dist/core/entities/workflow-model.js.map +1 -1
  9. package/dist/core/integrations.d.ts +25 -0
  10. package/dist/core/integrations.d.ts.map +1 -0
  11. package/dist/core/integrations.js +30 -0
  12. package/dist/core/integrations.js.map +1 -0
  13. package/dist/core/sync-definitions.d.ts +2 -2
  14. package/dist/core/sync-definitions.d.ts.map +1 -1
  15. package/dist/core/sync-definitions.js +23 -14
  16. package/dist/core/sync-definitions.js.map +1 -1
  17. package/dist/core/workflow-model/constants.d.ts +1 -1
  18. package/dist/core/workflow-model/constants.d.ts.map +1 -1
  19. package/dist/core/workflow-model/constants.js +1 -1
  20. package/dist/core/workflow-model/constants.js.map +1 -1
  21. package/dist/core/workflow-model/normalize-job-checkout.d.ts +2 -2
  22. package/dist/core/workflow-model/normalize-job-checkout.d.ts.map +1 -1
  23. package/dist/core/workflow-model/normalize-job-checkout.js +2 -6
  24. package/dist/core/workflow-model/normalize-job-checkout.js.map +1 -1
  25. package/dist/core/workflow-model/normalize-job-success.d.ts +2 -1
  26. package/dist/core/workflow-model/normalize-job-success.d.ts.map +1 -1
  27. package/dist/core/workflow-model/normalize-job-success.js +2 -1
  28. package/dist/core/workflow-model/normalize-job-success.js.map +1 -1
  29. package/dist/db/db.d.ts +2 -2
  30. package/dist/db/schema/sync-states.d.ts +1 -1
  31. package/dist/index.d.ts +5 -7
  32. package/dist/index.d.ts.map +1 -1
  33. package/dist/index.js +10 -11
  34. package/dist/index.js.map +1 -1
  35. package/dist/presentation/index.d.ts +1 -1
  36. package/dist/presentation/index.d.ts.map +1 -1
  37. package/dist/presentation/index.js +1 -1
  38. package/dist/presentation/index.js.map +1 -1
  39. package/dist/presentation/inter-module.d.ts +4 -0
  40. package/dist/presentation/inter-module.d.ts.map +1 -0
  41. package/dist/presentation/inter-module.js +25 -0
  42. package/dist/presentation/inter-module.js.map +1 -0
  43. package/dist/presentation/routes/create-definition.d.ts +5 -6
  44. package/dist/presentation/routes/create-definition.d.ts.map +1 -1
  45. package/dist/presentation/routes/create-definition.js +7 -19
  46. package/dist/presentation/routes/create-definition.js.map +1 -1
  47. package/dist/presentation/routes/get-definition.d.ts +2 -1
  48. package/dist/presentation/routes/get-definition.d.ts.map +1 -1
  49. package/dist/presentation/routes/get-definition.js +26 -35
  50. package/dist/presentation/routes/get-definition.js.map +1 -1
  51. package/dist/presentation/routes/index.d.ts +1 -2
  52. package/dist/presentation/routes/index.d.ts.map +1 -1
  53. package/dist/presentation/routes/index.js +5 -6
  54. package/dist/presentation/routes/index.js.map +1 -1
  55. package/dist/presentation/routes/list-definitions.d.ts +2 -1
  56. package/dist/presentation/routes/list-definitions.d.ts.map +1 -1
  57. package/dist/presentation/routes/list-definitions.js +36 -45
  58. package/dist/presentation/routes/list-definitions.js.map +1 -1
  59. package/dist/presentation/routes/project-access.d.ts +10 -0
  60. package/dist/presentation/routes/project-access.d.ts.map +1 -0
  61. package/dist/presentation/routes/project-access.js +17 -0
  62. package/dist/presentation/routes/project-access.js.map +1 -0
  63. package/dist/temporal/activities/sync-activities.d.ts +3 -9
  64. package/dist/temporal/activities/sync-activities.d.ts.map +1 -1
  65. package/dist/temporal/activities/sync-activities.js +10 -14
  66. package/dist/temporal/activities/sync-activities.js.map +1 -1
  67. package/dist/temporal/workflows/index.bundle.js +17195 -3337
  68. package/dist/tsconfig.test.tsbuildinfo +1 -1
  69. package/package.json +16 -32
  70. package/src/core/entities/workflow-definition.ts +3 -6
  71. package/src/core/entities/workflow-model.ts +1 -162
  72. package/src/core/integrations.ts +39 -0
  73. package/src/core/sync-definitions.test.ts +19 -17
  74. package/src/core/sync-definitions.ts +26 -20
  75. package/src/core/workflow-model/constants.ts +1 -1
  76. package/src/core/workflow-model/normalize-job-checkout.ts +2 -5
  77. package/src/core/workflow-model/normalize-job-success.ts +2 -1
  78. package/src/db/definitions.test.ts +68 -7
  79. package/src/index.ts +12 -31
  80. package/src/presentation/index.ts +1 -1
  81. package/src/presentation/inter-module.ts +25 -0
  82. package/src/presentation/routes/create-definition.test.ts +59 -63
  83. package/src/presentation/routes/create-definition.ts +15 -34
  84. package/src/presentation/routes/get-definition.test.ts +14 -8
  85. package/src/presentation/routes/get-definition.ts +25 -28
  86. package/src/presentation/routes/index.test.ts +8 -1
  87. package/src/presentation/routes/index.ts +5 -7
  88. package/src/presentation/routes/list-definitions.test.ts +8 -7
  89. package/src/presentation/routes/list-definitions.ts +33 -36
  90. package/src/presentation/routes/project-access.ts +16 -0
  91. package/src/temporal/activities/sync-activities.test.ts +14 -6
  92. package/src/temporal/activities/sync-activities.ts +20 -33
  93. package/tsconfig.build.tsbuildinfo +1 -1
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@shipfox/api-definitions",
3
3
  "license": "MIT",
4
- "version": "5.0.0",
4
+ "version": "7.1.0",
5
5
  "repository": {
6
6
  "type": "git",
7
7
  "url": "git+https://github.com/ShipfoxHQ/shipfox.git",
@@ -30,47 +30,31 @@
30
30
  "js-yaml": "^4.1.0",
31
31
  "yaml": "^2.8.3",
32
32
  "zod": "^4.4.3",
33
- "@shipfox/api-agent-dto": "5.0.0",
34
- "@shipfox/api-auth-context": "5.0.0",
35
- "@shipfox/api-definitions-dto": "5.0.0",
36
- "@shipfox/api-integration-core": "5.0.0",
37
- "@shipfox/api-integration-core-dto": "5.0.0",
38
- "@shipfox/api-projects": "5.0.0",
39
- "@shipfox/api-projects-dto": "5.0.0",
40
- "@shipfox/api-secrets-dto": "5.0.0",
33
+ "@shipfox/api-agent-dto": "6.0.0",
34
+ "@shipfox/api-auth-context": "7.1.0",
35
+ "@shipfox/api-definitions-dto": "6.0.0",
36
+ "@shipfox/api-integration-core-dto": "6.0.0",
37
+ "@shipfox/api-projects-dto": "6.0.0",
38
+ "@shipfox/api-secrets-dto": "6.0.0",
41
39
  "@shipfox/config": "1.2.2",
42
40
  "@shipfox/expression": "1.1.3",
43
- "@shipfox/node-drizzle": "0.3.1",
44
- "@shipfox/node-fastify": "0.2.3",
45
- "@shipfox/node-module": "0.3.2",
46
- "@shipfox/node-opentelemetry": "0.5.2",
47
- "@shipfox/node-outbox": "0.2.3",
41
+ "@shipfox/inter-module": "0.2.0",
42
+ "@shipfox/node-drizzle": "0.3.2",
43
+ "@shipfox/node-fastify": "0.3.0",
44
+ "@shipfox/node-error-monitoring": "0.2.0",
45
+ "@shipfox/node-module": "0.5.0",
46
+ "@shipfox/node-opentelemetry": "0.6.0",
47
+ "@shipfox/node-outbox": "0.2.4",
48
48
  "@shipfox/node-postgres": "0.4.2",
49
- "@shipfox/node-temporal": "0.3.1",
49
+ "@shipfox/node-temporal": "0.4.0",
50
50
  "@shipfox/runner-labels": "0.1.1",
51
51
  "@shipfox/workflow-document": "2.1.1"
52
52
  },
53
- "devDependencies": {
54
- "@temporalio/client": "1.18.1",
55
- "@temporalio/testing": "1.18.1",
56
- "@temporalio/worker": "1.18.1",
57
- "@types/js-yaml": "^4.0.9",
58
- "@types/pg": "^8.15.5",
59
- "drizzle-kit": "^0.31.10",
60
- "fastify": "^5.3.3",
61
- "fastify-type-provider-zod": "^6.0.0",
62
- "fishery": "^2.4.0",
63
- "@shipfox/biome": "1.8.2",
64
- "@shipfox/regex": "0.2.2",
65
- "@shipfox/swc": "1.2.6",
66
- "@shipfox/ts-config": "1.3.8",
67
- "@shipfox/typescript": "1.1.7",
68
- "@shipfox/vitest": "1.2.3"
69
- },
70
53
  "scripts": {
71
54
  "build": "shipfox-swc && shipfox-temporal-bundle dist/temporal/workflows/index.js",
72
55
  "check": "shipfox-biome-check",
73
56
  "check:fix": "shipfox-biome-check --write",
57
+ "depcruise": "shipfox-depcruise",
74
58
  "test": "shipfox-vitest-run",
75
59
  "test:watch": "shipfox-vitest-watch",
76
60
  "type": "shipfox-tsc-check",
@@ -1,12 +1,9 @@
1
+ import type {WorkflowModel, WorkflowSourceSnapshot} from '@shipfox/api-definitions-dto';
1
2
  import type {WorkflowDocument} from '@shipfox/workflow-document';
2
- import type {WorkflowModel} from './workflow-model.js';
3
3
 
4
- export type WorkflowSpec = WorkflowDocument;
4
+ export type {WorkflowSourceSnapshot};
5
5
 
6
- export interface WorkflowSourceSnapshot {
7
- content: string;
8
- format: 'yaml';
9
- }
6
+ export type WorkflowSpec = WorkflowDocument;
10
7
 
11
8
  export interface WorkflowDefinitionPayload {
12
9
  document: WorkflowDocument;
@@ -1,162 +1 @@
1
- import type {
2
- ExpressionType,
3
- OutputDeclarations,
4
- ResolvedFieldSegment,
5
- WorkflowExpression,
6
- } from '@shipfox/expression';
7
- import type {AgentThinking, Harness} from '@shipfox/workflow-document';
8
-
9
- export type WorkflowFieldTemplate = readonly ResolvedFieldSegment[];
10
- export type WorkflowEnvTemplates = Readonly<Record<string, WorkflowFieldTemplate>>;
11
- export type WorkflowOutputTemplates = Readonly<Record<string, WorkflowFieldTemplate>>;
12
-
13
- export interface WorkflowModel {
14
- readonly kind: 'workflow';
15
- readonly name: string;
16
- readonly env?: Readonly<Record<string, string>>;
17
- readonly templates?: {
18
- readonly env?: WorkflowEnvTemplates;
19
- };
20
- readonly triggers: readonly WorkflowModelTrigger[];
21
- readonly jobs: readonly WorkflowModelJob[];
22
- readonly dependencies: readonly WorkflowModelDependency[];
23
- }
24
-
25
- export interface WorkflowModelTrigger {
26
- readonly id: string;
27
- readonly key: string;
28
- readonly source: string;
29
- readonly event: string;
30
- readonly inputs?: Readonly<Record<string, unknown>>;
31
- readonly filter?: string;
32
- readonly config?: Readonly<Record<string, unknown>>;
33
- }
34
-
35
- export interface WorkflowModelJob {
36
- readonly id: string;
37
- readonly key: string;
38
- readonly mode: WorkflowModelJobMode;
39
- readonly runner: readonly string[];
40
- readonly runnerTemplates?: readonly WorkflowFieldTemplate[];
41
- readonly checkout: WorkflowModelJobCheckout;
42
- readonly if?: WorkflowExpression;
43
- readonly success?: string;
44
- readonly outputs?: WorkflowOutputTemplates;
45
- readonly outputTypes?: Readonly<Record<string, ExpressionType>>;
46
- readonly executionTimeoutMs?: number;
47
- readonly listening?: WorkflowModelJobListening;
48
- readonly name?: WorkflowFieldTemplate;
49
- readonly env?: Readonly<Record<string, string>>;
50
- readonly templates?: {
51
- readonly env?: WorkflowEnvTemplates;
52
- };
53
- readonly dependencies: readonly string[];
54
- readonly steps: readonly WorkflowModelStep[];
55
- }
56
-
57
- export type WorkflowModelJobMode = 'one_shot' | 'listening';
58
-
59
- export interface WorkflowModelJobListening {
60
- readonly on: readonly WorkflowModelListeningTrigger[];
61
- readonly until?: readonly WorkflowModelListeningTrigger[];
62
- readonly timeoutMs?: number;
63
- readonly maxExecutions?: number;
64
- readonly batch?: WorkflowModelListeningBatch;
65
- readonly onResolve: 'finish' | 'cancel';
66
- }
67
-
68
- export interface WorkflowModelListeningTrigger {
69
- readonly source: string;
70
- readonly event: string;
71
- readonly inputs?: Readonly<Record<string, unknown>>;
72
- readonly filter?: string;
73
- }
74
-
75
- export interface WorkflowModelListeningBatch {
76
- readonly debounceMs?: number;
77
- readonly maxSize?: number;
78
- readonly maxWaitMs?: number;
79
- }
80
-
81
- export interface WorkflowModelJobCheckout {
82
- readonly permissions: {readonly contents: 'read' | 'write'};
83
- readonly persistCredentials: boolean;
84
- }
85
-
86
- export type WorkflowModelStep = WorkflowModelRunStep | WorkflowModelAgentStep;
87
-
88
- interface WorkflowModelStepBase {
89
- readonly id: string;
90
- readonly key?: string;
91
- readonly if?: WorkflowExpression;
92
- readonly name?: string;
93
- readonly outputs?: OutputDeclarations;
94
- readonly gate?: WorkflowModelStepGate;
95
- readonly sourceLocation?: WorkflowSourceLocation;
96
- }
97
-
98
- export interface WorkflowModelRunStep extends WorkflowModelStepBase {
99
- readonly kind: 'run';
100
- readonly command: WorkflowModelRunCommand;
101
- readonly env?: Readonly<Record<string, string>>;
102
- readonly templates?: {
103
- readonly command?: WorkflowFieldTemplate;
104
- readonly name?: WorkflowFieldTemplate;
105
- readonly env?: WorkflowEnvTemplates;
106
- };
107
- }
108
-
109
- export interface WorkflowModelAgentStep extends WorkflowModelStepBase {
110
- readonly kind: 'agent';
111
- readonly harness?: Harness;
112
- readonly model?: string;
113
- readonly provider?: string;
114
- readonly thinking?: AgentThinking;
115
- readonly tools?: readonly string[];
116
- readonly integrations?: readonly WorkflowModelStepIntegration[];
117
- readonly prompt: string;
118
- readonly templates?: {
119
- readonly prompt?: WorkflowFieldTemplate;
120
- readonly model?: WorkflowFieldTemplate;
121
- readonly provider?: WorkflowFieldTemplate;
122
- readonly name?: WorkflowFieldTemplate;
123
- };
124
- }
125
-
126
- export interface WorkflowModelStepIntegration {
127
- readonly connection?: string;
128
- readonly include: readonly string[];
129
- readonly exclude?: readonly string[];
130
- readonly allowWrite: boolean;
131
- }
132
-
133
- export interface WorkflowSourceLocation {
134
- readonly startLine: number;
135
- readonly endLine: number;
136
- }
137
-
138
- export type WorkflowStepSourceLocationMap = ReadonlyMap<
139
- string,
140
- ReadonlyMap<number, WorkflowSourceLocation>
141
- >;
142
-
143
- export interface WorkflowModelRunCommand {
144
- readonly kind: 'shell';
145
- readonly value: string;
146
- }
147
-
148
- export interface WorkflowModelStepGate {
149
- readonly success?: WorkflowExpression;
150
- readonly onFailure?: WorkflowModelStepFailureAction;
151
- }
152
-
153
- export interface WorkflowModelStepFailureAction {
154
- readonly restartFrom: string;
155
- readonly feedback?: string;
156
- readonly feedbackTemplate?: WorkflowFieldTemplate;
157
- }
158
-
159
- export interface WorkflowModelDependency {
160
- readonly from: string;
161
- readonly to: string;
162
- }
1
+ export type * from '@shipfox/api-definitions-dto';
@@ -0,0 +1,39 @@
1
+ import {
2
+ type IntegrationsModuleClient,
3
+ integrationsInterModuleContract,
4
+ } from '@shipfox/api-integration-core-dto';
5
+
6
+ export type DefinitionsSourceControl = {
7
+ resolveRepository: IntegrationsModuleClient['resolveSourceRepository'];
8
+ listFiles: IntegrationsModuleClient['listSourceFiles'];
9
+ fetchFile: IntegrationsModuleClient['fetchSourceFile'];
10
+ };
11
+
12
+ export function createDefinitionsSourceControl(
13
+ integrations: IntegrationsModuleClient,
14
+ ): DefinitionsSourceControl {
15
+ return {
16
+ resolveRepository: integrations.resolveSourceRepository,
17
+ listFiles: integrations.listSourceFiles,
18
+ fetchFile: integrations.fetchSourceFile,
19
+ };
20
+ }
21
+
22
+ export async function loadIntegrationValidationContext(
23
+ integrations: IntegrationsModuleClient,
24
+ workspaceId: string,
25
+ defaultConnectionId: string,
26
+ ) {
27
+ const context = await integrations.getAgentToolsContext({workspaceId, defaultConnectionId});
28
+ return {
29
+ agentToolSelectionCatalogs: new Map(
30
+ context.selectionCatalogs.map(({provider, selectors}) => [provider, {selectors}]),
31
+ ),
32
+ workspaceConnectionSnapshot: new Map(
33
+ context.workspaceConnections.map(({slug, ...connection}) => [slug, connection]),
34
+ ),
35
+ defaultConnectionSlug: context.defaultConnection?.slug,
36
+ };
37
+ }
38
+
39
+ export {integrationsInterModuleContract};
@@ -1,10 +1,6 @@
1
- import {
2
- IntegrationConnectionInactiveError,
3
- IntegrationConnectionNotFoundError,
4
- IntegrationConnectionWorkspaceMismatchError,
5
- IntegrationProviderError,
6
- type IntegrationSourceControlService,
7
- } from '@shipfox/api-integration-core';
1
+ import type {IntegrationSourceControlService} from '@shipfox/api-integration-core';
2
+ import {integrationsInterModuleContract} from '@shipfox/api-integration-core-dto';
3
+ import {createInterModuleKnownError} from '@shipfox/inter-module';
8
4
  import {LOWERCASE_SHA256_HEX_RE} from '@shipfox/regex';
9
5
  import type {IntegrationValidationContext} from './entities/integration-context.js';
10
6
  import {DefinitionSyncPermanentError} from './errors.js';
@@ -349,13 +345,15 @@ describe('classifySyncFailure', () => {
349
345
  ['malformed-provider-response', false, 'provider-malformed-response'],
350
346
  ['content-too-large', false, 'content-too-large'],
351
347
  ['too-many-files', false, 'too-many-files'],
352
- ])('maps IntegrationProviderError(%s) to retryable=%s code=%s', (reason, retryable, code) => {
353
- const result = classifySyncFailure(
354
- // biome-ignore lint/suspicious/noExplicitAny: enumerated reason
355
- new IntegrationProviderError(reason as any, 'boom'),
348
+ ])('maps provider failures (%s) to retryable=%s code=%s', (reason, retryable, code) => {
349
+ const error = createInterModuleKnownError(
350
+ integrationsInterModuleContract.methods.resolveSourceRepository,
351
+ 'provider-failure',
352
+ {reason},
356
353
  );
354
+ const result = classifySyncFailure(error);
357
355
 
358
- expect(result).toEqual({code, message: 'boom', retryable});
356
+ expect(result).toEqual({code, message: error.message, retryable});
359
357
  });
360
358
 
361
359
  it('classifies DefinitionSyncPermanentError as non-retryable', () => {
@@ -367,11 +365,15 @@ describe('classifySyncFailure', () => {
367
365
  });
368
366
 
369
367
  it.each([
370
- () => new IntegrationConnectionNotFoundError('connection-1'),
371
- () => new IntegrationConnectionInactiveError('connection-1'),
372
- () => new IntegrationConnectionWorkspaceMismatchError('connection-1'),
373
- ])('classifies connection lifecycle errors as non-retryable connection-unavailable', (build) => {
374
- const error = build();
368
+ 'connection-not-found',
369
+ 'connection-inactive',
370
+ 'connection-workspace-mismatch',
371
+ ] as const)('classifies %s as non-retryable connection-unavailable', (code) => {
372
+ const error = createInterModuleKnownError(
373
+ integrationsInterModuleContract.methods.resolveSourceRepository,
374
+ code,
375
+ {connectionId: '00000000-0000-0000-0000-000000000000'},
376
+ );
375
377
 
376
378
  const result = classifySyncFailure(error);
377
379
 
@@ -1,18 +1,16 @@
1
1
  import {createHash} from 'node:crypto';
2
2
  import {
3
- IntegrationConnectionInactiveError,
4
- IntegrationConnectionNotFoundError,
5
- IntegrationConnectionWorkspaceMismatchError,
6
- IntegrationProviderError,
7
- type IntegrationSourceControlService,
3
+ integrationsInterModuleContract,
8
4
  MAX_REPOSITORY_FILE_BYTES,
9
- } from '@shipfox/api-integration-core';
5
+ } from '@shipfox/api-integration-core-dto';
6
+ import {isInterModuleKnownError} from '@shipfox/inter-module';
10
7
  import {boundedMap} from '@shipfox/node-module';
11
8
  import type {IntegrationValidationContext} from './entities/integration-context.js';
12
9
  import type {DefinitionSyncErrorCode} from './entities/sync-state.js';
13
10
  import type {WorkflowDefinitionPayload} from './entities/workflow-definition.js';
14
11
  import {DefinitionParseError, DefinitionSyncPermanentError} from './errors.js';
15
12
  import {hasAgentStepIntegrations} from './has-agent-step-integrations.js';
13
+ import type {DefinitionsSourceControl} from './integrations.js';
16
14
  import {parseDefinition} from './parse-definition.js';
17
15
 
18
16
  export const WORKFLOW_PREFIX = '.shipfox/workflows/';
@@ -24,7 +22,7 @@ export interface SyncSourceContext {
24
22
  workspaceId: string;
25
23
  sourceConnectionId: string;
26
24
  sourceExternalRepositoryId: string;
27
- sourceControl: IntegrationSourceControlService;
25
+ sourceControl: DefinitionsSourceControl;
28
26
  }
29
27
 
30
28
  export interface ResolvedSyncSource {
@@ -173,19 +171,27 @@ export function classifySyncFailure(error: unknown): SyncFailureClassification {
173
171
  if (error instanceof DefinitionSyncPermanentError) {
174
172
  return {code: error.code, message: error.message, retryable: false};
175
173
  }
176
- if (
177
- error instanceof IntegrationConnectionNotFoundError ||
178
- error instanceof IntegrationConnectionInactiveError ||
179
- error instanceof IntegrationConnectionWorkspaceMismatchError
180
- ) {
181
- return {code: 'connection-unavailable', message: error.message, retryable: false};
182
- }
183
- if (error instanceof IntegrationProviderError) {
184
- return {
185
- code: providerErrorCode(error.reason),
186
- message: error.message,
187
- retryable: isProviderReasonRetryable(error.reason),
188
- };
174
+ const methods = [
175
+ integrationsInterModuleContract.methods.resolveSourceRepository,
176
+ integrationsInterModuleContract.methods.listSourceFiles,
177
+ integrationsInterModuleContract.methods.fetchSourceFile,
178
+ ] as const;
179
+ for (const method of methods) {
180
+ if (!isInterModuleKnownError(method, error)) continue;
181
+ if (
182
+ error.code === 'connection-not-found' ||
183
+ error.code === 'connection-inactive' ||
184
+ error.code === 'connection-workspace-mismatch'
185
+ ) {
186
+ return {code: 'connection-unavailable', message: error.message, retryable: false};
187
+ }
188
+ if (error.code === 'provider-failure') {
189
+ return {
190
+ code: providerErrorCode(error.details.reason),
191
+ message: error.message,
192
+ retryable: isProviderReasonRetryable(error.details.reason),
193
+ };
194
+ }
189
195
  }
190
196
  return {
191
197
  code: 'unknown',
@@ -1 +1 @@
1
- export const DEFAULT_RUN_TIMEOUT_MS = 30 * 24 * 60 * 60 * 1000;
1
+ export {DEFAULT_RUN_TIMEOUT_MS} from '@shipfox/api-definitions-dto';
@@ -1,10 +1,7 @@
1
+ import {DEFAULT_JOB_CHECKOUT, type WorkflowModelJobCheckout} from '@shipfox/api-definitions-dto';
1
2
  import type {WorkflowDocumentJobCheckout} from '@shipfox/workflow-document';
2
- import type {WorkflowModelJobCheckout} from '../entities/workflow-model.js';
3
3
 
4
- export const DEFAULT_JOB_CHECKOUT: WorkflowModelJobCheckout = {
5
- permissions: {contents: 'read'},
6
- persistCredentials: true,
7
- };
4
+ export {DEFAULT_JOB_CHECKOUT};
8
5
 
9
6
  export function normalizeJobCheckout(
10
7
  checkout: WorkflowDocumentJobCheckout | undefined,
@@ -1,8 +1,9 @@
1
+ import {DEFAULT_JOB_SUCCESS} from '@shipfox/api-definitions-dto';
1
2
  import type {ExpressionTypeEnvironment} from '@shipfox/expression';
2
3
  import type {WorkflowModelValidationIssue} from './invalid-workflow-model-error.js';
3
4
  import {validatePredicateExpression} from './validate-predicate-expression.js';
4
5
 
5
- export const DEFAULT_JOB_SUCCESS = "!executions.exists(e, e.status == 'failed')";
6
+ export {DEFAULT_JOB_SUCCESS};
6
7
 
7
8
  export function normalizeJobSuccess(params: {
8
9
  source: string | undefined;
@@ -1,15 +1,19 @@
1
1
  import {DEFINITION_RESOLVED} from '@shipfox/api-definitions-dto';
2
2
  import {
3
3
  BATCH_SIZE,
4
+ countPendingOutboxRows as countPendingOutboxRowsFromRegistry,
5
+ createOutboxRegistry,
4
6
  type DrainedEvent,
5
- drainAll,
6
- markDispatched,
7
+ drainAll as drainFromRegistry,
8
+ type ModulePublisher,
9
+ markDispatched as markDispatchedFromRegistry,
10
+ type OutboxDispatchClaim,
7
11
  type OutboxDispatchFailure,
8
- pruneDispatchedOutboxRows,
9
- recordDispatchFailure,
10
- registerPublisher,
11
- renewDispatchClaim,
12
- resetPublishers,
12
+ type PruneDispatchedOutboxRowsOptions,
13
+ pruneDispatchedOutboxRows as pruneFromRegistry,
14
+ recordDispatchFailure as recordFailureFromRegistry,
15
+ registerPublisher as registerInRegistry,
16
+ renewDispatchClaim as renewFromRegistry,
13
17
  } from '@shipfox/node-module';
14
18
  import {sql} from 'drizzle-orm';
15
19
  import type {WorkflowDefinitionPayload} from '#core/entities/workflow-definition.js';
@@ -27,6 +31,44 @@ import {workflowDefinitions} from './schema/definitions.js';
27
31
  import {definitionsOutbox} from './schema/outbox.js';
28
32
 
29
33
  const MS_PER_DAY = 24 * 60 * 60 * 1_000;
34
+ let outboxRegistry = createOutboxRegistry();
35
+
36
+ function resetPublishers(): void {
37
+ outboxRegistry = createOutboxRegistry();
38
+ }
39
+
40
+ function registerPublisher(publisher: ModulePublisher): void {
41
+ registerInRegistry(outboxRegistry, publisher);
42
+ }
43
+
44
+ function drainAll(options?: {partition?: {workerIndex: number; workerCount: number}}) {
45
+ return drainFromRegistry(outboxRegistry, options);
46
+ }
47
+
48
+ function countPendingOutboxRows() {
49
+ return countPendingOutboxRowsFromRegistry(outboxRegistry);
50
+ }
51
+
52
+ function markDispatched(source: string, claims: string[] | OutboxDispatchClaim[]) {
53
+ return markDispatchedFromRegistry(outboxRegistry, source, claims);
54
+ }
55
+
56
+ function renewDispatchClaim(source: string, claim: OutboxDispatchClaim) {
57
+ return renewFromRegistry(outboxRegistry, source, claim);
58
+ }
59
+
60
+ function recordDispatchFailure(
61
+ source: string,
62
+ id: string,
63
+ failure: OutboxDispatchFailure,
64
+ claimExpiresAt?: Date,
65
+ ) {
66
+ return recordFailureFromRegistry(outboxRegistry, source, id, failure, claimExpiresAt);
67
+ }
68
+
69
+ function pruneDispatchedOutboxRows(options: PruneDispatchedOutboxRowsOptions) {
70
+ return pruneFromRegistry(outboxRegistry, options);
71
+ }
30
72
 
31
73
  function definitionFields(name = 'Test Workflow'): WorkflowDefinitionPayload {
32
74
  const document = {
@@ -740,6 +782,25 @@ describe('definition queries', () => {
740
782
  .where(sql`${definitionsOutbox.payload}->>'projectId' = ${projectId}`);
741
783
  });
742
784
 
785
+ test('countPendingOutboxRows counts pending rows but excludes dispatched and dead-lettered rows', async () => {
786
+ await insertOutboxRow({projectId, marker: 'pending'});
787
+ await insertOutboxRow({projectId, marker: 'dispatched', dispatchedAt: new Date()});
788
+ await insertOutboxRow({projectId, marker: 'dead-lettered', deadLetteredAt: new Date()});
789
+
790
+ const pendingRows = await countPendingOutboxRows();
791
+
792
+ expect(pendingRows).toBe(1);
793
+ });
794
+
795
+ test('countPendingOutboxRows sums pending rows across every registered publisher', async () => {
796
+ registerPublisher({name: 'definitions-second', table: definitionsOutbox, db: () => db()});
797
+ await insertOutboxRow({projectId, marker: 'pending'});
798
+
799
+ const pendingRows = await countPendingOutboxRows();
800
+
801
+ expect(pendingRows).toBe(2);
802
+ });
803
+
743
804
  test('drainAll returns undispatched outbox events', async () => {
744
805
  await upsertDefinition({
745
806
  projectId,
package/src/index.ts CHANGED
@@ -5,12 +5,8 @@ import {
5
5
  type DefinitionsEventMap,
6
6
  definitionsEventSchemas,
7
7
  } from '@shipfox/api-definitions-dto';
8
- import type {
9
- AgentToolSelectionCatalogs,
10
- GetIntegrationConnectionByIdFn,
11
- IntegrationSourceControlService,
12
- LoadWorkspaceConnectionSnapshot,
13
- } from '@shipfox/api-integration-core';
8
+ import type {IntegrationsModuleClient} from '@shipfox/api-integration-core-dto';
9
+ import type {ProjectsModuleClient} from '@shipfox/api-projects-dto';
14
10
  import {
15
11
  PROJECT_SOURCE_BOUND,
16
12
  PROJECT_SOURCE_COMMIT_OBSERVED,
@@ -18,8 +14,10 @@ import {
18
14
  } from '@shipfox/api-projects-dto';
19
15
  import {type ShipfoxModule, subscriberFactory} from '@shipfox/node-module';
20
16
  import {logger} from '@shipfox/node-opentelemetry';
17
+ import {createDefinitionsSourceControl} from '#core/integrations.js';
21
18
  import {db, definitionsOutbox, migrationsPath} from '#db/index.js';
22
19
  import {createDefinitionRoutes} from '#presentation/index.js';
20
+ import {createDefinitionsInterModulePresentation} from '#presentation/inter-module.js';
23
21
  import {
24
22
  onProjectSourceBound,
25
23
  onProjectSourceCommitObserved,
@@ -42,7 +40,6 @@ export {
42
40
  normalizeWorkflowDocument,
43
41
  } from '#core/index.js';
44
42
  export {db, definitionsOutbox, getDefinitionById, migrationsPath} from '#db/index.js';
45
- export {routes} from '#presentation/index.js';
46
43
 
47
44
  const packageRoot = resolve(dirname(fileURLToPath(import.meta.url)), '..');
48
45
  const workflowsPath = resolve(packageRoot, 'dist/temporal/workflows/index.js');
@@ -50,37 +47,20 @@ const workflowsPath = resolve(packageRoot, 'dist/temporal/workflows/index.js');
50
47
  const subscriber = subscriberFactory<DefinitionsEventMap & ProjectsEventMap>();
51
48
 
52
49
  export interface CreateDefinitionsModuleOptions {
53
- sourceControl: IntegrationSourceControlService;
54
- agentToolSelectionCatalogs?: AgentToolSelectionCatalogs | undefined;
55
- loadWorkspaceConnectionSnapshot?: LoadWorkspaceConnectionSnapshot | undefined;
56
- getIntegrationConnectionById?: GetIntegrationConnectionByIdFn | undefined;
50
+ projects: ProjectsModuleClient;
51
+ integrations: IntegrationsModuleClient;
57
52
  }
58
53
 
59
54
  export function createDefinitionsModule({
60
- sourceControl,
61
- agentToolSelectionCatalogs,
62
- loadWorkspaceConnectionSnapshot,
63
- getIntegrationConnectionById,
55
+ projects,
56
+ integrations,
64
57
  }: CreateDefinitionsModuleOptions): ShipfoxModule {
65
- const integrationValidation =
66
- agentToolSelectionCatalogs === undefined ||
67
- loadWorkspaceConnectionSnapshot === undefined ||
68
- getIntegrationConnectionById === undefined
69
- ? undefined
70
- : {
71
- agentToolSelectionCatalogs,
72
- loadWorkspaceConnectionSnapshot,
73
- getIntegrationConnectionById,
74
- };
58
+ const sourceControl = createDefinitionsSourceControl(integrations);
75
59
 
76
60
  return {
77
61
  name: 'definitions',
78
62
  database: {db, migrationsPath},
79
- routes: createDefinitionRoutes({
80
- agentToolSelectionCatalogs,
81
- loadWorkspaceConnectionSnapshot,
82
- getIntegrationConnectionById,
83
- }),
63
+ routes: createDefinitionRoutes({projects, integrations}),
84
64
  publishers: [
85
65
  {name: 'definitions', table: definitionsOutbox, db, eventSchemas: definitionsEventSchemas},
86
66
  ],
@@ -96,9 +76,10 @@ export function createDefinitionsModule({
96
76
  {
97
77
  taskQueue: DEFINITIONS_TASK_QUEUE,
98
78
  workflowsPath,
99
- activities: () => createDefinitionSyncActivities(sourceControl, integrationValidation),
79
+ activities: () => createDefinitionSyncActivities(sourceControl, integrations),
100
80
  workflows: [],
101
81
  },
102
82
  ],
83
+ interModulePresentations: [createDefinitionsInterModulePresentation()],
103
84
  };
104
85
  }
@@ -1 +1 @@
1
- export {createDefinitionRoutes, definitionRoutes as routes} from './routes/index.js';
1
+ export {createDefinitionRoutes} from './routes/index.js';
@@ -0,0 +1,25 @@
1
+ import {createWorkflowModelSnapshot} from '@shipfox/api-definitions-dto';
2
+ import {definitionsInterModuleContract} from '@shipfox/api-definitions-dto/inter-module';
3
+ import {defineInterModulePresentation, type InterModulePresentation} from '@shipfox/inter-module';
4
+ import {getDefinitionById} from '#db/definitions.js';
5
+
6
+ export function createDefinitionsInterModulePresentation(): InterModulePresentation<
7
+ typeof definitionsInterModuleContract
8
+ > {
9
+ return defineInterModulePresentation(definitionsInterModuleContract, {
10
+ getDefinitionForWorkflowRun: async ({definitionId}) => {
11
+ const definition = await getDefinitionById(definitionId);
12
+ if (!definition) return {definition: null};
13
+
14
+ return {
15
+ definition: {
16
+ id: definition.id,
17
+ projectId: definition.projectId,
18
+ name: definition.name,
19
+ model: createWorkflowModelSnapshot(definition.model),
20
+ sourceSnapshot: definition.sourceSnapshot,
21
+ },
22
+ };
23
+ },
24
+ });
25
+ }