@abloatai/transaction 0.37.1 → 0.38.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/dist/ai-sdk/index.d.ts +3 -1
- package/dist/ai-sdk/index.d.ts.map +1 -1
- package/dist/ai-sdk/index.js +2 -1
- package/dist/ai-sdk/index.js.map +1 -1
- package/dist/ai-sdk/modelTools.d.ts +67 -0
- package/dist/ai-sdk/modelTools.d.ts.map +1 -0
- package/dist/ai-sdk/modelTools.js +89 -0
- package/dist/ai-sdk/modelTools.js.map +1 -0
- package/dist/ai-sdk/toolOptions.d.ts +8 -0
- package/dist/ai-sdk/toolOptions.d.ts.map +1 -0
- package/dist/ai-sdk/toolOptions.js +2 -0
- package/dist/ai-sdk/toolOptions.js.map +1 -0
- package/dist/ai-sdk/{coordinatedTool.d.ts → updateTool.d.ts} +21 -28
- package/dist/ai-sdk/updateTool.d.ts.map +1 -0
- package/dist/ai-sdk/{coordinatedTool.js → updateTool.js} +39 -50
- package/dist/ai-sdk/updateTool.js.map +1 -0
- package/dist/auth/capability.d.ts +6 -0
- package/dist/auth/capability.d.ts.map +1 -1
- package/dist/auth/capability.js +4 -0
- package/dist/auth/capability.js.map +1 -1
- package/dist/auth/capabilityLifecycle.d.ts +3 -0
- package/dist/auth/capabilityLifecycle.d.ts.map +1 -1
- package/dist/auth/identity.d.ts +6 -4
- package/dist/auth/identity.d.ts.map +1 -1
- package/dist/auth/identity.js +7 -11
- package/dist/auth/identity.js.map +1 -1
- package/dist/auth/schemas.d.ts +5 -5
- package/dist/auth/schemas.d.ts.map +1 -1
- package/dist/auth/schemas.js +5 -2
- package/dist/auth/schemas.js.map +1 -1
- package/dist/auth/sessionMint.d.ts.map +1 -1
- package/dist/auth/sessionMint.js +3 -0
- package/dist/auth/sessionMint.js.map +1 -1
- package/dist/branches.d.ts +251 -0
- package/dist/branches.d.ts.map +1 -0
- package/dist/branches.js +108 -0
- package/dist/branches.js.map +1 -0
- package/dist/coordination/awaitClaimGrant.d.ts +2 -0
- package/dist/coordination/awaitClaimGrant.d.ts.map +1 -1
- package/dist/coordination/awaitClaimGrant.js +15 -2
- package/dist/coordination/awaitClaimGrant.js.map +1 -1
- package/dist/coordination/schema.d.ts +2 -0
- package/dist/coordination/schema.d.ts.map +1 -1
- package/dist/coordination/schema.js +2 -0
- package/dist/coordination/schema.js.map +1 -1
- package/dist/docs/catalog.d.ts +5 -5
- package/dist/docs/catalog.d.ts.map +1 -1
- package/dist/docs/catalog.js +12 -12
- package/dist/docs/catalog.js.map +1 -1
- package/dist/footprint.d.ts +3 -5
- package/dist/footprint.d.ts.map +1 -1
- package/dist/footprint.js +0 -0
- package/dist/footprint.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/keys/index.d.ts +15 -7
- package/dist/keys/index.d.ts.map +1 -1
- package/dist/keys/index.js +45 -14
- package/dist/keys/index.js.map +1 -1
- package/dist/schema/openapi.d.ts +1 -1
- package/dist/schema/openapi.d.ts.map +1 -1
- package/dist/schema/openapi.js +308 -29
- package/dist/schema/openapi.js.map +1 -1
- package/dist/server/commit.d.ts +2 -15
- package/dist/server/commit.d.ts.map +1 -1
- package/dist/source/adapter.d.ts +7 -2
- package/dist/source/adapter.d.ts.map +1 -1
- package/dist/source/adapter.js +4 -2
- package/dist/source/adapter.js.map +1 -1
- package/dist/source/adapterFactory.d.ts +10 -0
- package/dist/source/adapterFactory.d.ts.map +1 -0
- package/dist/source/adapterFactory.js +33 -0
- package/dist/source/adapterFactory.js.map +1 -0
- package/dist/source/adapterProfile.d.ts +55 -0
- package/dist/source/adapterProfile.d.ts.map +1 -0
- package/dist/source/adapterProfile.js +54 -0
- package/dist/source/adapterProfile.js.map +1 -0
- package/dist/source/adapters/drizzle.d.ts.map +1 -1
- package/dist/source/adapters/drizzle.js +5 -2
- package/dist/source/adapters/drizzle.js.map +1 -1
- package/dist/source/adapters/kysely.d.ts.map +1 -1
- package/dist/source/adapters/kysely.js +7 -4
- package/dist/source/adapters/kysely.js.map +1 -1
- package/dist/source/adapters/memory.d.ts.map +1 -1
- package/dist/source/adapters/memory.js +5 -2
- package/dist/source/adapters/memory.js.map +1 -1
- package/dist/source/adapters/prisma.d.ts.map +1 -1
- package/dist/source/adapters/prisma.js +5 -2
- package/dist/source/adapters/prisma.js.map +1 -1
- package/dist/source/conformance.d.ts.map +1 -1
- package/dist/source/conformance.js +18 -0
- package/dist/source/conformance.js.map +1 -1
- package/dist/source/index.d.ts +2 -0
- package/dist/source/index.d.ts.map +1 -1
- package/dist/source/index.js +2 -0
- package/dist/source/index.js.map +1 -1
- package/dist/source/types.d.ts +3 -14
- package/dist/source/types.d.ts.map +1 -1
- package/dist/source/types.js.map +1 -1
- package/dist/syncLog/contract.d.ts +1 -1
- package/dist/syncLog/contract.d.ts.map +1 -1
- package/dist/syncLog/contract.js +1 -2
- package/dist/syncLog/contract.js.map +1 -1
- package/dist/wire/accountResponses.d.ts +5 -9
- package/dist/wire/accountResponses.d.ts.map +1 -1
- package/dist/wire/accountResponses.js +5 -6
- package/dist/wire/accountResponses.js.map +1 -1
- package/dist/wire/delta.d.ts +11 -0
- package/dist/wire/delta.d.ts.map +1 -1
- package/dist/wire/delta.js +11 -0
- package/dist/wire/delta.js.map +1 -1
- package/dist/wire/inboundFrames.d.ts +2 -0
- package/dist/wire/inboundFrames.d.ts.map +1 -1
- package/package.json +8 -2
- package/src/ai-sdk/index.ts +19 -6
- package/src/ai-sdk/modelTools.ts +165 -0
- package/src/ai-sdk/toolOptions.ts +18 -0
- package/src/ai-sdk/{coordinatedTool.ts → updateTool.ts} +59 -69
- package/src/auth/capability.ts +4 -0
- package/src/auth/identity.ts +16 -16
- package/src/auth/schemas.ts +5 -2
- package/src/auth/sessionMint.ts +3 -0
- package/src/branches.ts +134 -0
- package/src/coordination/awaitClaimGrant.ts +18 -2
- package/src/coordination/schema.ts +2 -0
- package/src/docs/catalog.ts +24 -12
- package/src/footprint.ts +0 -0
- package/src/index.ts +1 -0
- package/src/keys/index.ts +56 -17
- package/src/schema/openapi.ts +366 -38
- package/src/server/commit.ts +2 -15
- package/src/source/adapter.ts +7 -2
- package/src/source/adapterFactory.ts +60 -0
- package/src/source/adapterProfile.ts +75 -0
- package/src/source/adapters/drizzle.ts +5 -2
- package/src/source/adapters/kysely.ts +10 -4
- package/src/source/adapters/memory.ts +5 -2
- package/src/source/adapters/prisma.ts +5 -2
- package/src/source/conformance.ts +18 -0
- package/src/source/index.ts +14 -0
- package/src/source/types.ts +3 -14
- package/src/syncLog/contract.ts +1 -2
- package/src/wire/accountResponses.ts +5 -6
- package/src/wire/delta.ts +11 -0
- package/dist/ai-sdk/coordinatedTool.d.ts.map +0 -1
- package/dist/ai-sdk/coordinatedTool.js.map +0 -1
package/src/schema/openapi.ts
CHANGED
|
@@ -45,6 +45,7 @@ import {
|
|
|
45
45
|
claimReorderReplySchema,
|
|
46
46
|
claimReleaseReplySchema,
|
|
47
47
|
} from '../wire/claims.js';
|
|
48
|
+
import { errorEnvelopeSchema } from '../wire/errorEnvelope.js';
|
|
48
49
|
import { modelReadResponseSchema, modelListResponseSchema } from '../wire/modelResponses.js';
|
|
49
50
|
import { modelMutationRequestSchema } from '../wire/modelMutations.js';
|
|
50
51
|
import { logListResponseSchema, logQuerySchema } from '../wire/feedEvent.js';
|
|
@@ -60,12 +61,21 @@ import {
|
|
|
60
61
|
capabilityRotationResponseSchema,
|
|
61
62
|
sessionRevocationResponseSchema,
|
|
62
63
|
} from '../auth/capabilityLifecycle.js';
|
|
64
|
+
import {
|
|
65
|
+
branchCredentialRequestSchema,
|
|
66
|
+
branchCredentialResponseSchema,
|
|
67
|
+
branchListResponseSchema,
|
|
68
|
+
branchResponseSchema,
|
|
69
|
+
branchStatusResponseSchema,
|
|
70
|
+
createBranchRequestSchema,
|
|
71
|
+
} from '../branches.js';
|
|
72
|
+
import { modelClaimSchema } from '../coordination/schema.js';
|
|
63
73
|
|
|
64
74
|
/** Options for {@link schemaToOpenApi} — the metadata stamped into the generated spec. */
|
|
65
75
|
export interface SchemaToOpenApiOptions {
|
|
66
76
|
/** Spec title. Default `"Ablo API"`. */
|
|
67
77
|
readonly title?: string;
|
|
68
|
-
/** Spec version.
|
|
78
|
+
/** Spec version. Published artifacts pass the current Ablo package version. */
|
|
69
79
|
readonly version?: string;
|
|
70
80
|
/** API base URL. Default `"https://api.abloatai.com/api"`. */
|
|
71
81
|
readonly serverUrl?: string;
|
|
@@ -73,6 +83,80 @@ export interface SchemaToOpenApiOptions {
|
|
|
73
83
|
|
|
74
84
|
type Json = Record<string, unknown>;
|
|
75
85
|
|
|
86
|
+
const HTTP_METHODS = new Set(['get', 'post', 'put', 'patch', 'delete']);
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Give generators a stable public name for every operation.
|
|
90
|
+
*
|
|
91
|
+
* The map is deliberately exhaustive: adding a route without naming it throws
|
|
92
|
+
* while rendering the document instead of letting each language generator
|
|
93
|
+
* invent a different method name.
|
|
94
|
+
*/
|
|
95
|
+
function applyOperationIds(
|
|
96
|
+
paths: Json,
|
|
97
|
+
operationIds: Readonly<Record<string, string>>,
|
|
98
|
+
): void {
|
|
99
|
+
const usedKeys = new Set<string>();
|
|
100
|
+
const usedIds = new Set<string>();
|
|
101
|
+
|
|
102
|
+
for (const [path, rawPathItem] of Object.entries(paths)) {
|
|
103
|
+
const pathItem = rawPathItem as Json;
|
|
104
|
+
for (const [method, rawOperation] of Object.entries(pathItem)) {
|
|
105
|
+
if (!HTTP_METHODS.has(method)) continue;
|
|
106
|
+
const key = `${method.toUpperCase()} ${path}`;
|
|
107
|
+
const operationId = operationIds[key];
|
|
108
|
+
if (!operationId) {
|
|
109
|
+
throw new Error(`OpenAPI operation ${key} has no stable operationId`);
|
|
110
|
+
}
|
|
111
|
+
if (usedIds.has(operationId)) {
|
|
112
|
+
throw new Error(`OpenAPI operationId ${operationId} is not unique`);
|
|
113
|
+
}
|
|
114
|
+
pathItem[method] = { ...(rawOperation as Json), operationId };
|
|
115
|
+
usedKeys.add(key);
|
|
116
|
+
usedIds.add(operationId);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const stale = Object.keys(operationIds).filter((key) => !usedKeys.has(key));
|
|
121
|
+
if (stale.length > 0) {
|
|
122
|
+
throw new Error(
|
|
123
|
+
`OpenAPI operationId map names operations that do not exist: ${stale.join(', ')}`,
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const ABLO_OPERATION_IDS: Readonly<Record<string, string>> = {
|
|
129
|
+
'GET /v1/models/{model}': 'listModelRows',
|
|
130
|
+
'POST /v1/models/{model}': 'createModelRow',
|
|
131
|
+
'GET /v1/models/{model}/{id}': 'getModelRow',
|
|
132
|
+
'PATCH /v1/models/{model}/{id}': 'updateModelRow',
|
|
133
|
+
'DELETE /v1/models/{model}/{id}': 'deleteModelRow',
|
|
134
|
+
'POST /v1/models/{model}/{id}/claim': 'acquireModelClaim',
|
|
135
|
+
'DELETE /v1/models/{model}/{id}/claim': 'releaseModelClaim',
|
|
136
|
+
'POST /v1/models/{model}/{id}/claim/heartbeat': 'heartbeatModelClaim',
|
|
137
|
+
'POST /v1/models/{model}/{id}/claim/reorder': 'reorderModelClaimQueue',
|
|
138
|
+
'POST /v1/ephemeral_keys': 'mintEphemeralKey',
|
|
139
|
+
'GET /v1/branches': 'listBranches',
|
|
140
|
+
'POST /v1/branches': 'createBranch',
|
|
141
|
+
'GET /v1/branches/{id}': 'getBranch',
|
|
142
|
+
'DELETE /v1/branches/{id}': 'deleteBranch',
|
|
143
|
+
'POST /v1/branches/{id}/credentials': 'mintBranchCredential',
|
|
144
|
+
'GET /v1/branches/{id}/status': 'getBranchStatus',
|
|
145
|
+
'GET /v1/claims': 'listClaims',
|
|
146
|
+
'POST /v1/claims': 'acquireClaim',
|
|
147
|
+
'POST /v1/claims/heartbeat': 'heartbeatClaims',
|
|
148
|
+
'GET /v1/claims/{claimId}': 'getClaim',
|
|
149
|
+
'DELETE /v1/claims/{claimId}': 'releaseClaim',
|
|
150
|
+
'POST /v1/claims/{claimId}/heartbeat': 'heartbeatClaim',
|
|
151
|
+
'POST /v1/capabilities': 'mintCapability',
|
|
152
|
+
'GET /v1/capabilities/{id}': 'getCapability',
|
|
153
|
+
'DELETE /v1/capabilities/{id}': 'revokeCapability',
|
|
154
|
+
'POST /v1/capabilities/{id}/rotate': 'rotateCapability',
|
|
155
|
+
'GET /v1/schema': 'getSchema',
|
|
156
|
+
'GET /v1/logs': 'listLogEntries',
|
|
157
|
+
'POST /v1/commits': 'commit',
|
|
158
|
+
};
|
|
159
|
+
|
|
76
160
|
function fieldSchema(f: FieldMeta): Json {
|
|
77
161
|
switch (f.type) {
|
|
78
162
|
case 'number':
|
|
@@ -106,6 +190,54 @@ const jsonResp = (description: string, schema: Json): Json => ({
|
|
|
106
190
|
description,
|
|
107
191
|
content: { 'application/json': { schema } },
|
|
108
192
|
});
|
|
193
|
+
const schemaRef = (name: string): Json => ({ $ref: `#/components/schemas/${name}` });
|
|
194
|
+
const namedResp = (description: string, name: string): Json =>
|
|
195
|
+
jsonResp(description, schemaRef(name));
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Zod emits JSON Schema 2020-12. OpenAPI 3.1 accepts it, but SDK generators
|
|
199
|
+
* intentionally support a narrower, portable subset:
|
|
200
|
+
*
|
|
201
|
+
* - `$schema` belongs on a standalone JSON Schema document, not every embedded
|
|
202
|
+
* OpenAPI Schema Object;
|
|
203
|
+
* - `propertyNames: { type: 'string' }` is redundant for JSON objects and is
|
|
204
|
+
* unsupported by Stainless;
|
|
205
|
+
* - `additionalProperties: {}` means "any JSON value", but Stainless requires
|
|
206
|
+
* the equivalent, explicit `true`.
|
|
207
|
+
*
|
|
208
|
+
* Keeping this normalization at the rendering boundary lets the Zod schemas
|
|
209
|
+
* remain the wire authority without publishing generator-hostile syntax.
|
|
210
|
+
*/
|
|
211
|
+
function portableSchema(value: unknown): unknown {
|
|
212
|
+
if (Array.isArray(value)) return value.map(portableSchema);
|
|
213
|
+
if (typeof value !== 'object' || value === null) return value;
|
|
214
|
+
|
|
215
|
+
const source = value as Record<string, unknown>;
|
|
216
|
+
const result: Record<string, unknown> = {};
|
|
217
|
+
for (const [key, child] of Object.entries(source)) {
|
|
218
|
+
if (key === '$schema') continue;
|
|
219
|
+
if (
|
|
220
|
+
key === 'propertyNames' &&
|
|
221
|
+
typeof child === 'object' &&
|
|
222
|
+
child !== null &&
|
|
223
|
+
Object.keys(child).length === 1 &&
|
|
224
|
+
(child as Record<string, unknown>).type === 'string'
|
|
225
|
+
) {
|
|
226
|
+
continue;
|
|
227
|
+
}
|
|
228
|
+
if (
|
|
229
|
+
key === 'additionalProperties' &&
|
|
230
|
+
typeof child === 'object' &&
|
|
231
|
+
child !== null &&
|
|
232
|
+
Object.keys(child).length === 0
|
|
233
|
+
) {
|
|
234
|
+
result[key] = true;
|
|
235
|
+
continue;
|
|
236
|
+
}
|
|
237
|
+
result[key] = portableSchema(child);
|
|
238
|
+
}
|
|
239
|
+
return result;
|
|
240
|
+
}
|
|
109
241
|
/**
|
|
110
242
|
* Derive a JSON Schema from a wire schema.
|
|
111
243
|
*
|
|
@@ -116,9 +248,11 @@ const jsonResp = (description: string, schema: Json): Json => ({
|
|
|
116
248
|
* call site rather than defaulted.
|
|
117
249
|
*/
|
|
118
250
|
const derive = (schema: z.ZodType, io: 'input' | 'output'): Json =>
|
|
119
|
-
|
|
251
|
+
portableSchema(
|
|
252
|
+
z.toJSONSchema(schema, { io, unrepresentable: 'any' }),
|
|
253
|
+
) as Json;
|
|
120
254
|
|
|
121
|
-
const commitReceipt = (): Json =>
|
|
255
|
+
const commitReceipt = (): Json => namedResp('Commit receipt', 'CommitReceipt');
|
|
122
256
|
|
|
123
257
|
const modelParam = (): Json => ({
|
|
124
258
|
name: 'model',
|
|
@@ -171,7 +305,16 @@ function withGenericRows(derived: Json): Json {
|
|
|
171
305
|
*/
|
|
172
306
|
function queryParams(schema: z.ZodType): Json[] {
|
|
173
307
|
const props = (derive(schema, 'input').properties ?? {}) as Record<string, Json>;
|
|
174
|
-
return Object.entries(props).map(([name, s]) => ({
|
|
308
|
+
return Object.entries(props).map(([name, s]) => ({
|
|
309
|
+
name,
|
|
310
|
+
in: 'query',
|
|
311
|
+
// Query strings arrive at the server as text, but OpenAPI describes the
|
|
312
|
+
// caller-facing value before serialization. Generated clients should take
|
|
313
|
+
// an integer here and encode it, not expose a stringly typed page size.
|
|
314
|
+
schema: name === 'limit'
|
|
315
|
+
? { type: 'integer', minimum: 1 }
|
|
316
|
+
: s,
|
|
317
|
+
}));
|
|
175
318
|
}
|
|
176
319
|
|
|
177
320
|
/**
|
|
@@ -180,7 +323,7 @@ function queryParams(schema: z.ZodType): Json[] {
|
|
|
180
323
|
* and a retry after a lost response deserves to know which it got.
|
|
181
324
|
*/
|
|
182
325
|
const releaseResp = (): Json =>
|
|
183
|
-
|
|
326
|
+
namedResp('Released', 'ClaimRelease');
|
|
184
327
|
|
|
185
328
|
const claimIdParam = (): Json => ({
|
|
186
329
|
name: 'claimId',
|
|
@@ -204,8 +347,12 @@ function envelope(options: SchemaToOpenApiOptions, description: string, paths: J
|
|
|
204
347
|
openapi: '3.1.0',
|
|
205
348
|
info: {
|
|
206
349
|
title: options.title ?? 'Ablo API',
|
|
207
|
-
version: options.version ?? '
|
|
350
|
+
version: options.version ?? 'development',
|
|
208
351
|
description,
|
|
352
|
+
license: {
|
|
353
|
+
name: 'Apache License 2.0',
|
|
354
|
+
identifier: 'Apache-2.0',
|
|
355
|
+
},
|
|
209
356
|
},
|
|
210
357
|
servers: [{ url: options.serverUrl ?? `${ABLO_HOSTED_HTTP_BASE_URL}/api` }],
|
|
211
358
|
security: [{ bearerAuth: [] }],
|
|
@@ -219,6 +366,90 @@ function envelope(options: SchemaToOpenApiOptions, description: string, paths: J
|
|
|
219
366
|
};
|
|
220
367
|
}
|
|
221
368
|
|
|
369
|
+
const ERROR_RESPONSES: Readonly<Record<string, string>> = {
|
|
370
|
+
'400': 'The request did not satisfy the published contract.',
|
|
371
|
+
'401': 'The Bearer credential is missing, malformed, or expired.',
|
|
372
|
+
'403': 'The credential does not authorize this operation.',
|
|
373
|
+
'404': 'The addressed resource does not exist in the credential scope.',
|
|
374
|
+
'409': 'The request conflicts with current claim, version, or idempotency state.',
|
|
375
|
+
'429': 'The caller exceeded an enforced rate limit.',
|
|
376
|
+
'500': 'The server could not complete the request.',
|
|
377
|
+
'503': 'A required service is temporarily unavailable.',
|
|
378
|
+
};
|
|
379
|
+
|
|
380
|
+
/**
|
|
381
|
+
* Every route passes failures through the same server error funnel. Publish
|
|
382
|
+
* that fact on every operation so generated transports decode all HTTP errors
|
|
383
|
+
* through `ErrorEnvelope`, rather than falling back to a language-specific raw
|
|
384
|
+
* response type.
|
|
385
|
+
*/
|
|
386
|
+
function attachCanonicalErrors(paths: Json): void {
|
|
387
|
+
for (const rawPathItem of Object.values(paths)) {
|
|
388
|
+
const pathItem = rawPathItem as Json;
|
|
389
|
+
for (const [method, rawOperation] of Object.entries(pathItem)) {
|
|
390
|
+
if (!HTTP_METHODS.has(method)) continue;
|
|
391
|
+
const operation = rawOperation as Json;
|
|
392
|
+
const responses = { ...(operation.responses as Json | undefined) };
|
|
393
|
+
for (const [status, description] of Object.entries(ERROR_RESPONSES)) {
|
|
394
|
+
responses[status] = namedResp(description, 'ErrorEnvelope');
|
|
395
|
+
}
|
|
396
|
+
responses.default = namedResp(
|
|
397
|
+
'An HTTP error not otherwise listed; decoded through the canonical envelope.',
|
|
398
|
+
'ErrorEnvelope',
|
|
399
|
+
);
|
|
400
|
+
operation.responses = responses;
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
function abloComponentSchemas(): Record<string, Json> {
|
|
406
|
+
const commitReceiptSchemaJson = derive(commitReceiptSchema, 'output');
|
|
407
|
+
commitReceiptSchemaJson.discriminator = { propertyName: 'status' };
|
|
408
|
+
|
|
409
|
+
const schemas: Record<string, Json> = {
|
|
410
|
+
Cursor: {
|
|
411
|
+
type: 'string',
|
|
412
|
+
description: 'An opaque pagination position. Copy it unchanged into the next request.',
|
|
413
|
+
},
|
|
414
|
+
ErrorEnvelope: derive(errorEnvelopeSchema, 'output'),
|
|
415
|
+
Claim: derive(modelClaimSchema, 'output'),
|
|
416
|
+
ClaimAcquire: {
|
|
417
|
+
oneOf: [schemaRef('ClaimAcquired'), schemaRef('ClaimQueued')],
|
|
418
|
+
discriminator: { propertyName: 'status' },
|
|
419
|
+
},
|
|
420
|
+
ClaimAcquired: derive(claimAcquiredResponseSchema, 'output'),
|
|
421
|
+
ClaimQueued: derive(claimQueuedResponseSchema, 'output'),
|
|
422
|
+
ClaimState: derive(claimStateSchema, 'output'),
|
|
423
|
+
ClaimList: derive(claimListResponseSchema, 'output'),
|
|
424
|
+
ClaimHeartbeat: derive(claimHeartbeatReplySchema, 'output'),
|
|
425
|
+
ClaimHeartbeatBatch: derive(claimHeartbeatBatchReplySchema, 'output'),
|
|
426
|
+
ClaimRelease: derive(claimReleaseReplySchema, 'output'),
|
|
427
|
+
ClaimReorder: derive(claimReorderReplySchema, 'output'),
|
|
428
|
+
CommitReceipt: commitReceiptSchemaJson,
|
|
429
|
+
ModelRead: withGenericRows(derive(modelReadResponseSchema, 'output')),
|
|
430
|
+
ModelPage: withGenericRows(derive(modelListResponseSchema, 'output')),
|
|
431
|
+
LogPage: derive(logListResponseSchema, 'output'),
|
|
432
|
+
SchemaRead: derive(schemaReadResponseSchema, 'output'),
|
|
433
|
+
};
|
|
434
|
+
|
|
435
|
+
// Reuse protocol concepts inside the named envelopes too. Without these
|
|
436
|
+
// refs, generators invent separate anonymous Claim and cursor models for
|
|
437
|
+
// every operation even though the server validates one canonical shape.
|
|
438
|
+
const properties = (name: string): Record<string, Json> =>
|
|
439
|
+
(schemas[name]?.properties ?? {}) as Record<string, Json>;
|
|
440
|
+
properties('ClaimAcquired').claim = schemaRef('Claim');
|
|
441
|
+
(properties('ClaimList').data as Json).items = schemaRef('Claim');
|
|
442
|
+
(properties('ModelRead').claims as Json).items = schemaRef('Claim');
|
|
443
|
+
|
|
444
|
+
for (const name of ['ClaimList', 'ModelPage', 'LogPage']) {
|
|
445
|
+
properties(name).next_cursor = {
|
|
446
|
+
oneOf: [schemaRef('Cursor'), { type: 'null' }],
|
|
447
|
+
};
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
return schemas;
|
|
451
|
+
}
|
|
452
|
+
|
|
222
453
|
/**
|
|
223
454
|
* The protocol reference: the route templates the server actually serves.
|
|
224
455
|
*
|
|
@@ -234,9 +465,9 @@ function envelope(options: SchemaToOpenApiOptions, description: string, paths: J
|
|
|
234
465
|
* {@link schemaToOpenApi}.
|
|
235
466
|
*/
|
|
236
467
|
export function abloOpenApi(options: SchemaToOpenApiOptions = {}): Json {
|
|
237
|
-
const rowResp =
|
|
468
|
+
const rowResp = namedResp(
|
|
238
469
|
'The row, with the watermark it was read at and who holds it.',
|
|
239
|
-
|
|
470
|
+
'ModelRead',
|
|
240
471
|
);
|
|
241
472
|
const tags = ['models'];
|
|
242
473
|
|
|
@@ -247,10 +478,10 @@ export function abloOpenApi(options: SchemaToOpenApiOptions = {}): Json {
|
|
|
247
478
|
summary: 'List rows of a model',
|
|
248
479
|
parameters: [modelParam(), ...queryParams(listQuerySchema)],
|
|
249
480
|
responses: {
|
|
250
|
-
'200':
|
|
481
|
+
'200': namedResp(
|
|
251
482
|
'A page of rows. `next_cursor` feeds `starting_after` on the next ' +
|
|
252
483
|
'call; `stamp` is the watermark the page was read at.',
|
|
253
|
-
|
|
484
|
+
'ModelPage',
|
|
254
485
|
),
|
|
255
486
|
},
|
|
256
487
|
},
|
|
@@ -292,16 +523,16 @@ export function abloOpenApi(options: SchemaToOpenApiOptions = {}): Json {
|
|
|
292
523
|
parameters: [modelParam(), idParam()],
|
|
293
524
|
requestBody: optionalJsonBody(derive(claimRequestSchema, 'input')),
|
|
294
525
|
responses: {
|
|
295
|
-
'201':
|
|
526
|
+
'201': namedResp(
|
|
296
527
|
'The lease is yours. `claim.fenceToken` is set when the coordinator ' +
|
|
297
528
|
'minted one; carry it on writes made under the lease.',
|
|
298
|
-
|
|
529
|
+
'ClaimAcquire',
|
|
299
530
|
),
|
|
300
|
-
'202':
|
|
531
|
+
'202': namedResp(
|
|
301
532
|
'The row was already held and you asked to queue. You are in line at ' +
|
|
302
533
|
'`position` — heartbeat to keep the slot, and poll ' +
|
|
303
534
|
'`GET /v1/claims/{claimId}` for the grant.',
|
|
304
|
-
|
|
535
|
+
'ClaimAcquire',
|
|
305
536
|
),
|
|
306
537
|
},
|
|
307
538
|
},
|
|
@@ -319,9 +550,9 @@ export function abloOpenApi(options: SchemaToOpenApiOptions = {}): Json {
|
|
|
319
550
|
parameters: [modelParam(), idParam()],
|
|
320
551
|
requestBody: optionalJsonBody(derive(claimHeartbeatRequestSchema, 'input')),
|
|
321
552
|
responses: {
|
|
322
|
-
'200':
|
|
553
|
+
'200': namedResp(
|
|
323
554
|
'Lease extended (or queued slot refreshed)',
|
|
324
|
-
|
|
555
|
+
'ClaimHeartbeat',
|
|
325
556
|
),
|
|
326
557
|
},
|
|
327
558
|
},
|
|
@@ -336,7 +567,7 @@ export function abloOpenApi(options: SchemaToOpenApiOptions = {}): Json {
|
|
|
336
567
|
parameters: [modelParam(), idParam()],
|
|
337
568
|
requestBody: optionalJsonBody(derive(claimReorderRequestSchema, 'input')),
|
|
338
569
|
responses: {
|
|
339
|
-
'200':
|
|
570
|
+
'200': namedResp('Reordered', 'ClaimReorder'),
|
|
340
571
|
},
|
|
341
572
|
},
|
|
342
573
|
},
|
|
@@ -354,6 +585,77 @@ export function abloOpenApi(options: SchemaToOpenApiOptions = {}): Json {
|
|
|
354
585
|
responses: { '201': jsonResp('The minted credential', derive(EphemeralKeyResponseSchema, 'output')) },
|
|
355
586
|
},
|
|
356
587
|
},
|
|
588
|
+
'/v1/branches': {
|
|
589
|
+
get: {
|
|
590
|
+
tags: ['branches'],
|
|
591
|
+
summary: 'List transaction branches for the credential project',
|
|
592
|
+
responses: {
|
|
593
|
+
'200': jsonResp(
|
|
594
|
+
'The root and every active child branch.',
|
|
595
|
+
derive(branchListResponseSchema, 'output'),
|
|
596
|
+
),
|
|
597
|
+
},
|
|
598
|
+
},
|
|
599
|
+
post: {
|
|
600
|
+
tags: ['branches'],
|
|
601
|
+
summary: 'Create an isolated child branch',
|
|
602
|
+
description:
|
|
603
|
+
'The returned id is immutable; retain it for automation. The slug is a project-scoped human handle.',
|
|
604
|
+
requestBody: jsonBody(derive(createBranchRequestSchema, 'input')),
|
|
605
|
+
responses: {
|
|
606
|
+
'201': jsonResp('The ready branch.', derive(branchResponseSchema, 'output')),
|
|
607
|
+
},
|
|
608
|
+
},
|
|
609
|
+
},
|
|
610
|
+
'/v1/branches/{id}': {
|
|
611
|
+
get: {
|
|
612
|
+
tags: ['branches'],
|
|
613
|
+
summary: 'Retrieve a branch by immutable id',
|
|
614
|
+
parameters: [idParam()],
|
|
615
|
+
responses: {
|
|
616
|
+
'200': jsonResp('The branch.', derive(branchResponseSchema, 'output')),
|
|
617
|
+
},
|
|
618
|
+
},
|
|
619
|
+
delete: {
|
|
620
|
+
tags: ['branches'],
|
|
621
|
+
summary: 'Delete a non-root branch and revoke its credentials',
|
|
622
|
+
parameters: [idParam()],
|
|
623
|
+
responses: {
|
|
624
|
+
'200': jsonResp('The deleted branch.', derive(branchResponseSchema, 'output')),
|
|
625
|
+
},
|
|
626
|
+
},
|
|
627
|
+
},
|
|
628
|
+
'/v1/branches/{id}/credentials': {
|
|
629
|
+
post: {
|
|
630
|
+
tags: ['branches', 'credentials'],
|
|
631
|
+
summary: 'Mint an expiring branch-bound test credential',
|
|
632
|
+
parameters: [idParam()],
|
|
633
|
+
requestBody: optionalJsonBody(
|
|
634
|
+
derive(branchCredentialRequestSchema, 'input'),
|
|
635
|
+
),
|
|
636
|
+
responses: {
|
|
637
|
+
'201': jsonResp(
|
|
638
|
+
'A one-time plaintext credential. Do not persist it in source control.',
|
|
639
|
+
derive(branchCredentialResponseSchema, 'output'),
|
|
640
|
+
),
|
|
641
|
+
},
|
|
642
|
+
},
|
|
643
|
+
},
|
|
644
|
+
'/v1/branches/{id}/status': {
|
|
645
|
+
get: {
|
|
646
|
+
tags: ['branches'],
|
|
647
|
+
summary: 'Diagnose one branch plane',
|
|
648
|
+
description:
|
|
649
|
+
'Returns branch lifecycle, active schema, compatibility with the parent schema, safe datasource coordinates, and readiness blockers.',
|
|
650
|
+
parameters: [idParam()],
|
|
651
|
+
responses: {
|
|
652
|
+
'200': jsonResp(
|
|
653
|
+
'The complete branch readiness view.',
|
|
654
|
+
derive(branchStatusResponseSchema, 'output'),
|
|
655
|
+
),
|
|
656
|
+
},
|
|
657
|
+
},
|
|
658
|
+
},
|
|
357
659
|
'/v1/claims': {
|
|
358
660
|
get: {
|
|
359
661
|
tags: ['claims'],
|
|
@@ -365,9 +667,9 @@ export function abloOpenApi(options: SchemaToOpenApiOptions = {}): Json {
|
|
|
365
667
|
'request names both `model` and `id` — a wait line belongs to one row.',
|
|
366
668
|
parameters: queryParams(claimListQuerySchema),
|
|
367
669
|
responses: {
|
|
368
|
-
'200':
|
|
670
|
+
'200': namedResp(
|
|
369
671
|
'Live claims, and the wait line behind the named row.',
|
|
370
|
-
|
|
672
|
+
'ClaimList',
|
|
371
673
|
),
|
|
372
674
|
},
|
|
373
675
|
},
|
|
@@ -379,13 +681,13 @@ export function abloOpenApi(options: SchemaToOpenApiOptions = {}): Json {
|
|
|
379
681
|
'row in `target` instead of the URL. Answers identically.',
|
|
380
682
|
requestBody: jsonBody(derive(claimRequestSchema, 'input')),
|
|
381
683
|
responses: {
|
|
382
|
-
'201':
|
|
684
|
+
'201': namedResp(
|
|
383
685
|
'The lease is yours.',
|
|
384
|
-
|
|
686
|
+
'ClaimAcquire',
|
|
385
687
|
),
|
|
386
|
-
'202':
|
|
688
|
+
'202': namedResp(
|
|
387
689
|
'Already held, and you asked to queue. You are in line at `position`.',
|
|
388
|
-
|
|
690
|
+
'ClaimAcquire',
|
|
389
691
|
),
|
|
390
692
|
},
|
|
391
693
|
},
|
|
@@ -400,9 +702,9 @@ export function abloOpenApi(options: SchemaToOpenApiOptions = {}): Json {
|
|
|
400
702
|
'credential holds on this plane.',
|
|
401
703
|
requestBody: optionalJsonBody(derive(claimHeartbeatRequestSchema, 'input')),
|
|
402
704
|
responses: {
|
|
403
|
-
'200':
|
|
705
|
+
'200': namedResp(
|
|
404
706
|
'One ack per lease extended.',
|
|
405
|
-
|
|
707
|
+
'ClaimHeartbeatBatch',
|
|
406
708
|
),
|
|
407
709
|
},
|
|
408
710
|
},
|
|
@@ -416,7 +718,7 @@ export function abloOpenApi(options: SchemaToOpenApiOptions = {}): Json {
|
|
|
416
718
|
'was granted. `position` is advisory — a privileged reorder can move it ' +
|
|
417
719
|
'up — so branch on `status`, never on position.',
|
|
418
720
|
parameters: [claimIdParam()],
|
|
419
|
-
responses: { '200':
|
|
721
|
+
responses: { '200': namedResp('The claim state', 'ClaimState') },
|
|
420
722
|
},
|
|
421
723
|
delete: {
|
|
422
724
|
tags: ['claims'],
|
|
@@ -442,9 +744,9 @@ export function abloOpenApi(options: SchemaToOpenApiOptions = {}): Json {
|
|
|
442
744
|
parameters: [claimIdParam()],
|
|
443
745
|
requestBody: optionalJsonBody(derive(claimHeartbeatRequestSchema, 'input')),
|
|
444
746
|
responses: {
|
|
445
|
-
'200':
|
|
747
|
+
'200': namedResp(
|
|
446
748
|
'Lease extended, or queued slot refreshed.',
|
|
447
|
-
|
|
749
|
+
'ClaimHeartbeat',
|
|
448
750
|
),
|
|
449
751
|
},
|
|
450
752
|
},
|
|
@@ -518,9 +820,9 @@ export function abloOpenApi(options: SchemaToOpenApiOptions = {}): Json {
|
|
|
518
820
|
'the shape once and poll the hashes after: a refetch only ever answers ' +
|
|
519
821
|
'a push.',
|
|
520
822
|
responses: {
|
|
521
|
-
'200':
|
|
823
|
+
'200': namedResp(
|
|
522
824
|
'The deployed schema, or `active: false` when nothing is pushed.',
|
|
523
|
-
|
|
825
|
+
'SchemaRead',
|
|
524
826
|
),
|
|
525
827
|
},
|
|
526
828
|
},
|
|
@@ -537,11 +839,11 @@ export function abloOpenApi(options: SchemaToOpenApiOptions = {}): Json {
|
|
|
537
839
|
'widen what it sees by asking.',
|
|
538
840
|
parameters: queryParams(logQuerySchema),
|
|
539
841
|
responses: {
|
|
540
|
-
'200':
|
|
842
|
+
'200': namedResp(
|
|
541
843
|
'A page of the feed, oldest first. Entries are discriminated on ' +
|
|
542
844
|
'`object`, so a reader that meets an entry kind it does not know ' +
|
|
543
845
|
'can skip it and keep paging.',
|
|
544
|
-
|
|
846
|
+
'LogPage',
|
|
545
847
|
),
|
|
546
848
|
},
|
|
547
849
|
},
|
|
@@ -557,13 +859,16 @@ export function abloOpenApi(options: SchemaToOpenApiOptions = {}): Json {
|
|
|
557
859
|
},
|
|
558
860
|
};
|
|
559
861
|
|
|
862
|
+
applyOperationIds(paths, ABLO_OPERATION_IDS);
|
|
863
|
+
attachCanonicalErrors(paths);
|
|
864
|
+
|
|
560
865
|
return envelope(
|
|
561
866
|
options,
|
|
562
867
|
'The Ablo transaction layer: commit, read, and claim. `{model}` is any model ' +
|
|
563
868
|
'from your pushed schema — the routes are the same whichever it is. ' +
|
|
564
869
|
'Authenticate every request with your API key as a Bearer token.',
|
|
565
870
|
paths,
|
|
566
|
-
|
|
871
|
+
abloComponentSchemas(),
|
|
567
872
|
);
|
|
568
873
|
}
|
|
569
874
|
|
|
@@ -582,6 +887,7 @@ export function schemaToOpenApi<S extends SchemaRecord>(
|
|
|
582
887
|
const models: SchemaRecord = schema.models;
|
|
583
888
|
const paths: Json = {};
|
|
584
889
|
const schemas: Record<string, Json> = {};
|
|
890
|
+
const operationIds: Record<string, string> = {};
|
|
585
891
|
|
|
586
892
|
for (const [key, def] of Object.entries(models)) {
|
|
587
893
|
const ref: Json = { $ref: `#/components/schemas/${pascal(key)}` };
|
|
@@ -597,7 +903,24 @@ export function schemaToOpenApi<S extends SchemaRecord>(
|
|
|
597
903
|
schemas[pascal(key)] = { type: 'object', properties, required };
|
|
598
904
|
const createBody = jsonBody({ type: 'object', properties: createProps });
|
|
599
905
|
|
|
600
|
-
|
|
906
|
+
const collectionPath = `/v1/models/${key}`;
|
|
907
|
+
const rowPath = `/v1/models/${key}/{id}`;
|
|
908
|
+
const claimPath = `/v1/models/${key}/{id}/claim`;
|
|
909
|
+
const heartbeatPath = `/v1/models/${key}/{id}/claim/heartbeat`;
|
|
910
|
+
const reorderPath = `/v1/models/${key}/{id}/claim/reorder`;
|
|
911
|
+
const modelName = pascal(key);
|
|
912
|
+
|
|
913
|
+
operationIds[`GET ${collectionPath}`] = `list${modelName}Rows`;
|
|
914
|
+
operationIds[`POST ${collectionPath}`] = `create${modelName}Row`;
|
|
915
|
+
operationIds[`GET ${rowPath}`] = `get${modelName}Row`;
|
|
916
|
+
operationIds[`PATCH ${rowPath}`] = `update${modelName}Row`;
|
|
917
|
+
operationIds[`DELETE ${rowPath}`] = `delete${modelName}Row`;
|
|
918
|
+
operationIds[`POST ${claimPath}`] = `acquire${modelName}Claim`;
|
|
919
|
+
operationIds[`DELETE ${claimPath}`] = `release${modelName}Claim`;
|
|
920
|
+
operationIds[`POST ${heartbeatPath}`] = `heartbeat${modelName}Claim`;
|
|
921
|
+
operationIds[`POST ${reorderPath}`] = `reorder${modelName}ClaimQueue`;
|
|
922
|
+
|
|
923
|
+
paths[collectionPath] = {
|
|
601
924
|
get: {
|
|
602
925
|
tags: [key],
|
|
603
926
|
summary: `List ${key}`,
|
|
@@ -611,7 +934,7 @@ export function schemaToOpenApi<S extends SchemaRecord>(
|
|
|
611
934
|
},
|
|
612
935
|
post: { tags: [key], summary: `Create a ${key}`, requestBody: createBody, responses: { '200': commitReceipt() } },
|
|
613
936
|
};
|
|
614
|
-
paths[
|
|
937
|
+
paths[rowPath] = {
|
|
615
938
|
get: {
|
|
616
939
|
tags: [key],
|
|
617
940
|
summary: `Retrieve a ${key}`,
|
|
@@ -626,14 +949,14 @@ export function schemaToOpenApi<S extends SchemaRecord>(
|
|
|
626
949
|
patch: { tags: [key], summary: `Update a ${key}`, parameters: [idParam()], requestBody: createBody, responses: { '200': commitReceipt() } },
|
|
627
950
|
delete: { tags: [key], summary: `Delete a ${key}`, parameters: [idParam()], responses: { '200': commitReceipt() } },
|
|
628
951
|
};
|
|
629
|
-
paths[
|
|
952
|
+
paths[claimPath] = {
|
|
630
953
|
post: { tags: [key], summary: `Claim a ${key} (acquire lease)`, parameters: [idParam()], responses: { '200': jsonResp('Claim acquired', { type: 'object' }) } },
|
|
631
954
|
delete: { tags: [key], summary: `Release a ${key} claim`, parameters: [idParam()], responses: { '200': jsonResp('Released', { type: 'object' }) } },
|
|
632
955
|
};
|
|
633
|
-
paths[
|
|
956
|
+
paths[heartbeatPath] = {
|
|
634
957
|
post: { tags: [key], summary: `Heartbeat a held ${key} claim (extend the lease for long-running work)`, parameters: [idParam()], responses: { '200': jsonResp('Lease extended (or queued slot refreshed)', derive(claimHeartbeatReplySchema, 'output')) } },
|
|
635
958
|
};
|
|
636
|
-
paths[
|
|
959
|
+
paths[reorderPath] = {
|
|
637
960
|
post: { tags: [key], summary: `Reorder the ${key} wait-line (privileged)`, parameters: [idParam()], responses: { '200': jsonResp('Reordered', { type: 'object' }) } },
|
|
638
961
|
};
|
|
639
962
|
}
|
|
@@ -647,6 +970,11 @@ export function schemaToOpenApi<S extends SchemaRecord>(
|
|
|
647
970
|
responses: { '200': commitReceipt() },
|
|
648
971
|
},
|
|
649
972
|
};
|
|
973
|
+
operationIds['POST /v1/commits'] = 'commit';
|
|
974
|
+
applyOperationIds(paths, operationIds);
|
|
975
|
+
attachCanonicalErrors(paths);
|
|
976
|
+
|
|
977
|
+
Object.assign(schemas, abloComponentSchemas());
|
|
650
978
|
|
|
651
979
|
return envelope(
|
|
652
980
|
options,
|
package/src/server/commit.ts
CHANGED
|
@@ -16,7 +16,6 @@
|
|
|
16
16
|
import type { ParticipantKind, ConfirmationState } from '../log/syncDeltaRow.js';
|
|
17
17
|
import type { ParticipantRef } from '../wire/delta.js';
|
|
18
18
|
import type { CommitExecutionResultInput } from '../wire/commit.js';
|
|
19
|
-
import type { Environment } from '../environment.js';
|
|
20
19
|
import type { ReadDependency, TrackDependency } from '../coordination/schema.js';
|
|
21
20
|
|
|
22
21
|
export interface CommitContext {
|
|
@@ -27,21 +26,15 @@ export interface CommitContext {
|
|
|
27
26
|
*/
|
|
28
27
|
participantKind: ParticipantKind;
|
|
29
28
|
organizationId: string;
|
|
29
|
+
/** Immutable branch selected by the authenticated credential. */
|
|
30
|
+
branchId: string;
|
|
30
31
|
/**
|
|
31
32
|
* Project scope used to route source-mode storage. When omitted, the commit
|
|
32
33
|
* targets the organization's default project.
|
|
33
34
|
*/
|
|
34
35
|
projectId?: string;
|
|
35
|
-
/** Exact sandbox plane derived from the authenticated key, when present. */
|
|
36
|
-
sandboxId?: string;
|
|
37
36
|
/** Optional external account scope forwarded to storage resolvers. */
|
|
38
37
|
accountScope?: string;
|
|
39
|
-
/**
|
|
40
|
-
* The environment this commit runs in. Source-mode adapters forward it to the
|
|
41
|
-
* customer's handlers so that sandbox and production traffic can reach distinct
|
|
42
|
-
* customer-owned stores.
|
|
43
|
-
*/
|
|
44
|
-
environment?: Environment;
|
|
45
38
|
/**
|
|
46
39
|
* The sync groups this participant subscribes to, taken from the connection
|
|
47
40
|
* upgrade or the capability token. Each is appended to every delta's `sync_groups`
|
|
@@ -50,12 +43,6 @@ export interface CommitContext {
|
|
|
50
43
|
* commit fans out to just the organization and user groups.
|
|
51
44
|
*/
|
|
52
45
|
syncGroups?: readonly string[];
|
|
53
|
-
/**
|
|
54
|
-
* When true, the commit does not add `org:<organizationId>` to a delta's sync
|
|
55
|
-
* groups. Set this for sandbox writes, so that live organization subscribers do
|
|
56
|
-
* not receive test-environment changes.
|
|
57
|
-
*/
|
|
58
|
-
omitOrgSyncGroup?: boolean;
|
|
59
46
|
/**
|
|
60
47
|
* The participant on whose authority the actor acted. For a direct human commit
|
|
61
48
|
* this equals the actor; for an agent commit it is the human at the root of the
|