@startsimpli/api 0.5.20 → 0.5.22

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,159 +1,205 @@
1
- /**
2
- * Workflows API wrapper
1
+ /** Workflows API client — generic node-based workflow engine (epic startsim-xsh.11).
3
2
  *
4
- * Provides type-safe access to Django Workflows API
3
+ * Mirrors the other @startsimpli/api wrappers: an E-map of endpoints under
4
+ * `api/v1/workflows/`, the `client.fetch.{get,post,patch,delete}` convention,
5
+ * and the auto snake_case <-> camelCase transform (so all types here are
6
+ * camelCase).
7
+ *
8
+ * The execution detail type (`WorkflowExecutionDetail`) is deliberately shaped
9
+ * to match present-web's `run-to-builder-state.ts` `WorkflowRunDetail`, so a
10
+ * `getExecution(...)` response can be fed straight into `runToBuilderState`.
5
11
  */
6
12
 
13
+ import type { PaginatedResponse } from '../types';
7
14
  import type { ApiClient } from './api-client';
8
15
 
9
- export type WorkflowStatus = 'draft' | 'active' | 'paused';
16
+ const E = {
17
+ workflows: 'api/v1/workflows',
18
+ workflow: (id: string) => `api/v1/workflows/${id}`,
19
+ nodeTypes: 'api/v1/workflows/node-types',
20
+ execute: (id: string) => `api/v1/workflows/${id}/execute`,
21
+ executions: (id: string) => `api/v1/workflows/${id}/executions`,
22
+ execution: (executionId: string) => `api/v1/workflows/executions/${executionId}`,
23
+ cancel: (executionId: string) => `api/v1/workflows/executions/${executionId}/cancel`,
24
+ retry: (executionId: string) => `api/v1/workflows/executions/${executionId}/retry`,
25
+ clone: (id: string) => `api/v1/workflows/${id}/clone`,
26
+ publish: (id: string) => `api/v1/workflows/${id}/publish`,
27
+ versions: (id: string) => `api/v1/workflows/${id}/versions`,
28
+ };
29
+
30
+ /** Run lifecycle status — matches run-to-builder-state's `WorkflowRunStatus`. */
31
+ export type RunStatus =
32
+ | 'pending'
33
+ | 'running'
34
+ | 'completed'
35
+ | 'failed'
36
+ | 'cancelled'
37
+ | 'waiting';
38
+
39
+ /** Per-node status — matches run-to-builder-state's `WorkflowNodeStatus`. */
40
+ export type NodeStatus =
41
+ | 'idle'
42
+ | 'pending'
43
+ | 'running'
44
+ | 'success'
45
+ | 'error'
46
+ | 'skipped';
47
+
48
+ export type ExecutionMode = 'manual' | 'trigger' | 'webhook';
49
+
50
+ /** A node placed in a workflow graph (engine-defined shape). */
51
+ export interface WorkflowNode {
52
+ id: string;
53
+ type: string;
54
+ parameters?: Record<string, unknown>;
55
+ position?: [number, number];
56
+ [key: string]: unknown;
57
+ }
10
58
 
11
59
  export interface Workflow {
12
60
  id: string;
13
61
  name: string;
14
62
  description: string;
15
- team: string;
16
- createdBy: string | null;
17
- nodes: any[];
18
- connections: Record<string, any>;
19
- settings: Record<string, any>;
20
- staticData: Record<string, any>;
63
+ nodes: WorkflowNode[];
64
+ connections: Record<string, unknown>;
21
65
  isActive: boolean;
22
- version: number;
23
- isTemplate: boolean;
24
- templateSource: string | null;
25
- executionsCount: number;
26
- createdAt: string;
27
- updatedAt: string;
66
+ version?: number;
67
+ isTemplate?: boolean;
68
+ settings?: Record<string, unknown>;
69
+ executionsCount?: number;
70
+ createdAt?: string;
71
+ updatedAt?: string;
72
+ }
73
+
74
+ /** A registered node type the builder can place. */
75
+ export interface NodeTypeDef {
76
+ slug: string;
77
+ category: string;
78
+ label: string;
79
+ /** JSON-schema-ish description of the node's parameters. */
80
+ parameterSchema: Record<string, unknown>;
81
+ /** Names of the node's output connectors. */
82
+ outputLabels: string[];
28
83
  }
29
84
 
85
+ /** Lightweight execution summary (list rows). */
30
86
  export interface WorkflowExecution {
31
87
  id: string;
32
- workflow: string;
33
- workflowVersion: number;
34
- triggeredBy: string;
35
- status: 'pending' | 'running' | 'completed' | 'failed' | 'paused';
36
- mode: 'manual' | 'trigger' | 'webhook';
37
- contextData: Record<string, any>;
38
- state: Record<string, any>;
88
+ workflowId: string;
89
+ status: RunStatus;
90
+ mode: ExecutionMode;
91
+ source?: string;
39
92
  startedAt: string | null;
40
93
  completedAt: string | null;
41
- waitUntil: string | null;
42
- errorMessage: string | null;
43
- createdAt: string;
44
- updatedAt: string;
45
94
  }
46
95
 
47
- export interface WorkflowFilters {
48
- team?: string;
49
- isActive?: boolean;
50
- isTemplate?: boolean;
51
- page?: number;
52
- pageSize?: number;
53
- ordering?: string;
96
+ /** One node's execution record — matches run-to-builder-state's `RunNodeExecution`. */
97
+ export interface WorkflowNodeExecution {
98
+ nodeId: string;
99
+ nodeName?: string;
100
+ nodeType?: string;
101
+ status: NodeStatus;
102
+ executionOrder?: number;
103
+ output?: Record<string, unknown> | null;
104
+ }
105
+
106
+ /** Full execution detail. Structurally matches present-web's `WorkflowRunDetail`
107
+ * so a response can be passed directly to `runToBuilderState`. */
108
+ export interface WorkflowExecutionDetail {
109
+ status: RunStatus;
110
+ state: Record<string, unknown>;
111
+ nodeExecutions: WorkflowNodeExecution[];
112
+ contextData?: Record<string, unknown>;
54
113
  }
55
114
 
56
- export interface ExecuteWorkflowInput {
57
- contextData?: Record<string, any>;
58
- mode?: 'manual' | 'trigger' | 'webhook';
115
+ /** The contract returned by POST execute/retry: carries the executionId. */
116
+ export interface ExecuteWorkflowResponse {
117
+ executionId: string;
118
+ status: RunStatus;
59
119
  }
60
120
 
61
- export interface CreateWorkflowInput {
121
+ export interface WorkflowInput {
62
122
  name: string;
63
123
  description?: string;
64
- team: string;
65
- nodes?: any[];
66
- connections?: Record<string, any>;
67
- settings?: Record<string, any>;
124
+ nodes?: WorkflowNode[];
125
+ connections?: Record<string, unknown>;
68
126
  isActive?: boolean;
127
+ settings?: Record<string, unknown>;
128
+ }
129
+
130
+ export interface WorkflowListParams {
131
+ page?: number;
132
+ pageSize?: number;
133
+ search?: string;
134
+ ordering?: string;
135
+ isActive?: boolean;
136
+ isTemplate?: boolean;
137
+ [key: string]: unknown;
138
+ }
139
+
140
+ export interface ExecutionListParams {
141
+ page?: number;
142
+ pageSize?: number;
143
+ status?: RunStatus;
144
+ ordering?: string;
145
+ [key: string]: unknown;
69
146
  }
70
147
 
71
148
  export class WorkflowsApi {
72
149
  constructor(private client: ApiClient) {}
73
150
 
74
- /**
75
- * List workflows with optional filters
76
- */
77
- async list(filters?: WorkflowFilters) {
78
- const params = new URLSearchParams();
79
-
80
- if (filters?.team) params.append('team', filters.team);
81
- if (filters?.isActive !== undefined) params.append('isActive', String(filters.isActive));
82
- if (filters?.isTemplate !== undefined) params.append('isTemplate', String(filters.isTemplate));
83
- if (filters?.page) params.append('page', String(filters.page));
84
- if (filters?.pageSize) params.append('pageSize', String(filters.pageSize));
85
- if (filters?.ordering) params.append('ordering', filters.ordering);
86
-
87
- return this.client.get<{ results: Workflow[]; count: number; next: string | null; previous: string | null }>(
88
- `/api/v1/workflows/?${params.toString()}`
89
- );
151
+ // --- Workflows ---
152
+ listWorkflows(params?: WorkflowListParams): Promise<PaginatedResponse<Workflow>> {
153
+ return this.client.fetch.get<PaginatedResponse<Workflow>>(E.workflows, { params });
90
154
  }
91
-
92
- /**
93
- * Get workflow by ID
94
- */
95
- async get(id: string) {
96
- return this.client.get<Workflow>(`/api/v1/workflows/${id}/`);
155
+ getWorkflow(id: string): Promise<Workflow> {
156
+ return this.client.fetch.get<Workflow>(E.workflow(id));
97
157
  }
98
-
99
- /**
100
- * Create a new workflow
101
- */
102
- async create(data: CreateWorkflowInput) {
103
- return this.client.post<Workflow>('/api/v1/workflows/', data);
158
+ createWorkflow(data: WorkflowInput): Promise<Workflow> {
159
+ return this.client.fetch.post<Workflow>(E.workflows, data);
104
160
  }
105
-
106
- /**
107
- * Update workflow
108
- */
109
- async update(id: string, data: Partial<CreateWorkflowInput>) {
110
- return this.client.patch<Workflow>(`/api/v1/workflows/${id}/`, data);
161
+ updateWorkflow(id: string, data: Partial<WorkflowInput>): Promise<Workflow> {
162
+ return this.client.fetch.patch<Workflow>(E.workflow(id), data);
111
163
  }
112
-
113
- /**
114
- * Delete workflow
115
- */
116
- async delete(id: string) {
117
- return this.client.delete(`/api/v1/workflows/${id}/`);
164
+ deleteWorkflow(id: string): Promise<void> {
165
+ return this.client.fetch.delete<void>(E.workflow(id));
118
166
  }
119
167
 
120
- /**
121
- * Execute workflow
122
- */
123
- async execute(id: string, input?: ExecuteWorkflowInput) {
124
- return this.client.post<WorkflowExecution>(
125
- `/api/v1/workflows/${id}/execute/`,
126
- input || {}
127
- );
168
+ // --- Node type registry ---
169
+ getNodeTypes(): Promise<NodeTypeDef[]> {
170
+ return this.client.fetch.get<NodeTypeDef[]>(E.nodeTypes);
128
171
  }
129
172
 
130
- /**
131
- * Get workflow templates
132
- */
133
- async templates() {
134
- return this.client.get<Workflow[]>('/api/v1/workflows/templates/');
173
+ // --- Execution ---
174
+ /** Kick off a run. The response carries the executionId (the contract). */
175
+ executeWorkflow(id: string, contextData?: Record<string, unknown>): Promise<ExecuteWorkflowResponse> {
176
+ return this.client.fetch.post<ExecuteWorkflowResponse>(E.execute(id), { contextData });
135
177
  }
136
-
137
- /**
138
- * List workflow executions
139
- */
140
- async listExecutions(filters?: { workflow?: string; status?: string; page?: number; pageSize?: number }) {
141
- const params = new URLSearchParams();
142
-
143
- if (filters?.workflow) params.append('workflow', filters.workflow);
144
- if (filters?.status) params.append('status', filters.status);
145
- if (filters?.page) params.append('page', String(filters.page));
146
- if (filters?.pageSize) params.append('pageSize', String(filters.pageSize));
147
-
148
- return this.client.get<{ results: WorkflowExecution[]; count: number }>(
149
- `/api/v1/workflow-executions/?${params.toString()}`
150
- );
178
+ getExecution(executionId: string): Promise<WorkflowExecutionDetail> {
179
+ return this.client.fetch.get<WorkflowExecutionDetail>(E.execution(executionId));
180
+ }
181
+ listExecutions(
182
+ workflowId: string,
183
+ params?: ExecutionListParams,
184
+ ): Promise<PaginatedResponse<WorkflowExecution>> {
185
+ return this.client.fetch.get<PaginatedResponse<WorkflowExecution>>(E.executions(workflowId), { params });
186
+ }
187
+ cancelExecution(executionId: string): Promise<WorkflowExecution> {
188
+ return this.client.fetch.post<WorkflowExecution>(E.cancel(executionId), undefined);
189
+ }
190
+ /** Re-run a failed/cancelled execution; response carries the new executionId. */
191
+ retryExecution(executionId: string): Promise<ExecuteWorkflowResponse> {
192
+ return this.client.fetch.post<ExecuteWorkflowResponse>(E.retry(executionId), undefined);
151
193
  }
152
194
 
153
- /**
154
- * Get workflow execution details
155
- */
156
- async getExecution(id: string) {
157
- return this.client.get<WorkflowExecution>(`/api/v1/workflow-executions/${id}/`);
195
+ // --- Lifecycle ---
196
+ cloneWorkflow(id: string): Promise<Workflow> {
197
+ return this.client.fetch.post<Workflow>(E.clone(id), undefined);
198
+ }
199
+ publishWorkflow(id: string): Promise<Workflow> {
200
+ return this.client.fetch.post<Workflow>(E.publish(id), undefined);
201
+ }
202
+ getVersions(id: string): Promise<Workflow[]> {
203
+ return this.client.fetch.get<Workflow[]>(E.versions(id));
158
204
  }
159
205
  }
@@ -98,6 +98,28 @@ export type {
98
98
  ApolloEnrichmentSummary,
99
99
  } from './enrichment';
100
100
 
101
+ // Team / Company / Invitation / Domain-claim types (startsim-o7s)
102
+ export type {
103
+ Company,
104
+ AuthUserLike,
105
+ TeamRole,
106
+ Team,
107
+ TeamMember,
108
+ TeamInvitation,
109
+ DomainVerificationMethod,
110
+ EmailDomainClaim,
111
+ CompanyListParams,
112
+ UpdateCompanyInput,
113
+ TeamListParams,
114
+ BulkInviteEntry,
115
+ BulkInviteResult,
116
+ TeamInvitationListParams,
117
+ CreateTeamInvitationInput,
118
+ DomainClaimListParams,
119
+ CreateDomainClaimInput,
120
+ DomainVerifyEmailInitiateResponse,
121
+ } from './team';
122
+
101
123
  // Error types
102
124
  export type {
103
125
  FieldError,
@@ -0,0 +1,175 @@
1
+ /**
2
+ * Team / Company / Invitation / Domain-claim types.
3
+ *
4
+ * Mirrors the Django serializers in apps.companies / apps.teams /
5
+ * apps.email_domain_claims. The FetchWrapper transforms snake_case ↔ camelCase
6
+ * at the boundary, so types here are camelCase. startsim-o7s.
7
+ */
8
+
9
+ /** A Company is the top-level org tenant in StartSimpli. */
10
+ export interface Company {
11
+ id: string;
12
+ slug: string;
13
+ name: string;
14
+ /** Free-form per-company settings blob (feature flags + product prefs). */
15
+ settings: Record<string, unknown>;
16
+ /** Hydrated by `?expand=owner`; otherwise just present as a foreign-key id. */
17
+ owner?: AuthUserLike | null;
18
+ ownerId?: string;
19
+ createdAt: string;
20
+ updatedAt: string;
21
+ }
22
+
23
+ /**
24
+ * Trimmed AuthUser used by the team-mgmt serializers.
25
+ *
26
+ * Imported from @startsimpli/auth in practice, but we keep an internal shape
27
+ * here so @startsimpli/api stays decoupled from the auth package's exports.
28
+ * The real AuthUser is structurally compatible.
29
+ */
30
+ export interface AuthUserLike {
31
+ id: string;
32
+ email: string;
33
+ firstName?: string;
34
+ lastName?: string;
35
+ fullName?: string;
36
+ }
37
+
38
+ /** Role hierarchy on a TeamMember (mirrors backend `Role` choices). */
39
+ export type TeamRole = 'owner' | 'admin' | 'member' | 'viewer';
40
+
41
+ /** A Team scopes role assignments inside a Company. */
42
+ export interface Team {
43
+ id: string;
44
+ slug: string;
45
+ name: string;
46
+ companyId: string;
47
+ settings: Record<string, unknown>;
48
+ createdAt: string;
49
+ updatedAt: string;
50
+ }
51
+
52
+ /** A TeamMember pins a user into a Team at a specific role. */
53
+ export interface TeamMember {
54
+ id: string;
55
+ userId: string;
56
+ teamId: string;
57
+ role: TeamRole;
58
+ invitedById?: string | null;
59
+ joinedAt: string;
60
+ /** Optional hydrated user (when serializer expands it). */
61
+ user?: AuthUserLike;
62
+ }
63
+
64
+ /**
65
+ * Pending or accepted invitation to join a Team.
66
+ * `token` is write-only after create — surfaced only in the create response
67
+ * so apps can build accept-links.
68
+ */
69
+ export interface TeamInvitation {
70
+ id: string;
71
+ email: string;
72
+ teamId: string;
73
+ role: TeamRole;
74
+ /** Only present in the create-response. Backend redacts on subsequent reads. */
75
+ token?: string;
76
+ invitedById: string;
77
+ expiresAt: string;
78
+ acceptedAt?: string | null;
79
+ revokedAt?: string | null;
80
+ isExpired: boolean;
81
+ isAccepted: boolean;
82
+ createdAt: string;
83
+ updatedAt: string;
84
+ }
85
+
86
+ /** Verification method used for an EmailDomainClaim. */
87
+ export type DomainVerificationMethod = 'dns_txt' | 'email_attestation';
88
+
89
+ /**
90
+ * A Company's claim on an email domain — gates "anyone @acme.com auto-joins"
91
+ * behavior once verified. See startsim-gpu.
92
+ */
93
+ export interface EmailDomainClaim {
94
+ id: string;
95
+ companyId: string;
96
+ domain: string;
97
+ verified: boolean;
98
+ verificationMethod?: DomainVerificationMethod | null;
99
+ /** Only visible to the creator on POST — otherwise null/absent. */
100
+ verificationToken?: string | null;
101
+ verifiedAt?: string | null;
102
+ verifiedById?: string | null;
103
+ lastVerifiedCheckAt?: string | null;
104
+ createdAt: string;
105
+ updatedAt: string;
106
+ }
107
+
108
+ // ---- API request/payload shapes -------------------------------------------
109
+
110
+ export interface CompanyListParams {
111
+ page?: number;
112
+ pageSize?: number;
113
+ search?: string;
114
+ ordering?: string;
115
+ [key: string]: unknown;
116
+ }
117
+
118
+ export interface UpdateCompanyInput {
119
+ name?: string;
120
+ slug?: string;
121
+ settings?: Record<string, unknown>;
122
+ }
123
+
124
+ export interface TeamListParams {
125
+ page?: number;
126
+ pageSize?: number;
127
+ search?: string;
128
+ companyId?: string;
129
+ ordering?: string;
130
+ [key: string]: unknown;
131
+ }
132
+
133
+ export interface BulkInviteEntry {
134
+ email: string;
135
+ role: TeamRole;
136
+ }
137
+
138
+ export interface BulkInviteResult {
139
+ invited: TeamInvitation[];
140
+ /** Backend may return duplicates / failures per the bulk endpoint. */
141
+ skipped?: Array<{ email: string; reason: string }>;
142
+ }
143
+
144
+ export interface TeamInvitationListParams {
145
+ page?: number;
146
+ pageSize?: number;
147
+ teamId?: string;
148
+ ordering?: string;
149
+ [key: string]: unknown;
150
+ }
151
+
152
+ export interface CreateTeamInvitationInput {
153
+ email: string;
154
+ teamId: string;
155
+ role: TeamRole;
156
+ }
157
+
158
+ export interface DomainClaimListParams {
159
+ page?: number;
160
+ pageSize?: number;
161
+ companyId?: string;
162
+ verified?: boolean;
163
+ ordering?: string;
164
+ [key: string]: unknown;
165
+ }
166
+
167
+ export interface CreateDomainClaimInput {
168
+ companyId: string;
169
+ domain: string;
170
+ }
171
+
172
+ export interface DomainVerifyEmailInitiateResponse {
173
+ /** Backend confirms an attestation email has been queued. */
174
+ detail: string;
175
+ }
package/src/types/user.ts CHANGED
@@ -24,9 +24,28 @@ export interface UpdateProfileRequest {
24
24
  export interface ChangePasswordRequest {
25
25
  oldPassword: string;
26
26
  newPassword: string;
27
- newPasswordConfirm: string;
28
27
  }
29
28
 
30
29
  export interface ChangePasswordResponse {
31
30
  detail: string;
32
31
  }
32
+
33
+ /**
34
+ * Passwordless early-access registration — name + email only.
35
+ * Hits POST /api/v1/auth/early-register/ (public, no auth).
36
+ */
37
+ export interface EarlyRegisterRequest {
38
+ email: string;
39
+ firstName?: string;
40
+ lastName?: string;
41
+ }
42
+
43
+ export interface EarlyRegisterResponse {
44
+ user: {
45
+ id: string;
46
+ email: string;
47
+ firstName: string;
48
+ lastName: string;
49
+ };
50
+ message: string;
51
+ }