@sanoma-ai/workflows 0.0.0-stage → 0.2.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/LICENSE +105 -0
- package/README.md +544 -2
- package/dist/apply-steps.d.ts +6 -0
- package/dist/apply-steps.js +93 -0
- package/dist/apply-steps.js.map +1 -0
- package/dist/apply.d.ts +197 -0
- package/dist/apply.js +470 -0
- package/dist/apply.js.map +1 -0
- package/dist/approvals.d.ts +46 -0
- package/dist/approvals.js +111 -0
- package/dist/approvals.js.map +1 -0
- package/dist/ast.d.ts +57 -0
- package/dist/ast.js +81 -0
- package/dist/ast.js.map +1 -0
- package/dist/call.d.ts +37 -0
- package/dist/call.js +417 -0
- package/dist/call.js.map +1 -0
- package/dist/client.d.ts +203 -0
- package/dist/client.js +457 -0
- package/dist/client.js.map +1 -0
- package/dist/config.d.ts +126 -0
- package/dist/config.js +264 -0
- package/dist/config.js.map +1 -0
- package/dist/credentials.d.ts +55 -0
- package/dist/credentials.js +261 -0
- package/dist/credentials.js.map +1 -0
- package/dist/cucumber.d.ts +64 -0
- package/dist/cucumber.js +158 -0
- package/dist/cucumber.js.map +1 -0
- package/dist/datafile.d.ts +71 -0
- package/dist/datafile.js +380 -0
- package/dist/datafile.js.map +1 -0
- package/dist/db.d.ts +58 -0
- package/dist/db.js +179 -0
- package/dist/db.js.map +1 -0
- package/dist/define.d.ts +205 -0
- package/dist/define.js +103 -0
- package/dist/define.js.map +1 -0
- package/dist/describe.d.ts +141 -0
- package/dist/describe.js +135 -0
- package/dist/describe.js.map +1 -0
- package/dist/drift.d.ts +127 -0
- package/dist/drift.js +167 -0
- package/dist/drift.js.map +1 -0
- package/dist/env.d.ts +49 -0
- package/dist/env.js +83 -0
- package/dist/env.js.map +1 -0
- package/dist/errors.d.ts +88 -0
- package/dist/errors.js +148 -0
- package/dist/errors.js.map +1 -0
- package/dist/expectations.d.ts +26 -0
- package/dist/expectations.js +162 -0
- package/dist/expectations.js.map +1 -0
- package/dist/fake.d.ts +55 -0
- package/dist/fake.js +87 -0
- package/dist/fake.js.map +1 -0
- package/dist/fill.d.ts +9 -0
- package/dist/fill.js +17 -0
- package/dist/fill.js.map +1 -0
- package/dist/host.d.ts +56 -0
- package/dist/host.js +45 -0
- package/dist/host.js.map +1 -0
- package/dist/index.d.ts +23 -0
- package/dist/index.js +29 -0
- package/dist/index.js.map +1 -0
- package/dist/ledger.d.ts +217 -0
- package/dist/ledger.js +251 -0
- package/dist/ledger.js.map +1 -0
- package/dist/lint.d.ts +32 -0
- package/dist/lint.js +168 -0
- package/dist/lint.js.map +1 -0
- package/dist/lockfile.d.ts +36 -0
- package/dist/lockfile.js +69 -0
- package/dist/lockfile.js.map +1 -0
- package/dist/log.d.ts +24 -0
- package/dist/log.js +83 -0
- package/dist/log.js.map +1 -0
- package/dist/op.d.ts +260 -0
- package/dist/op.js +188 -0
- package/dist/op.js.map +1 -0
- package/dist/outline.d.ts +179 -0
- package/dist/outline.js +506 -0
- package/dist/outline.js.map +1 -0
- package/dist/paths.d.ts +11 -0
- package/dist/paths.js +30 -0
- package/dist/paths.js.map +1 -0
- package/dist/plugin.d.ts +28 -0
- package/dist/plugin.js +25 -0
- package/dist/plugin.js.map +1 -0
- package/dist/policy.d.ts +114 -0
- package/dist/policy.js +60 -0
- package/dist/policy.js.map +1 -0
- package/dist/resource.d.ts +378 -0
- package/dist/resource.js +430 -0
- package/dist/resource.js.map +1 -0
- package/dist/resources.d.ts +77 -0
- package/dist/resources.js +244 -0
- package/dist/resources.js.map +1 -0
- package/dist/run.d.ts +101 -0
- package/dist/run.js +7 -0
- package/dist/run.js.map +1 -0
- package/dist/sandbox.d.ts +33 -0
- package/dist/sandbox.js +127 -0
- package/dist/sandbox.js.map +1 -0
- package/dist/scenario.d.ts +221 -0
- package/dist/scenario.js +1104 -0
- package/dist/scenario.js.map +1 -0
- package/dist/scenarios.d.ts +55 -0
- package/dist/scenarios.js +71 -0
- package/dist/scenarios.js.map +1 -0
- package/dist/secret.d.ts +42 -0
- package/dist/secret.js +58 -0
- package/dist/secret.js.map +1 -0
- package/dist/shared.d.ts +69 -0
- package/dist/shared.js +84 -0
- package/dist/shared.js.map +1 -0
- package/dist/source.d.ts +58 -0
- package/dist/source.js +54 -0
- package/dist/source.js.map +1 -0
- package/dist/steps.d.ts +76 -0
- package/dist/steps.js +23 -0
- package/dist/steps.js.map +1 -0
- package/dist/version.d.ts +29 -0
- package/dist/version.js +76 -0
- package/dist/version.js.map +1 -0
- package/dist/worker.d.ts +37 -0
- package/dist/worker.js +228 -0
- package/dist/worker.js.map +1 -0
- package/dist/writer.d.ts +35 -0
- package/dist/writer.js +201 -0
- package/dist/writer.js.map +1 -0
- package/oxlint.json +128 -0
- package/package.json +96 -3
- package/src/apply-steps.ts +110 -0
- package/src/apply.ts +626 -0
- package/src/approvals.ts +157 -0
- package/src/ast.ts +115 -0
- package/src/call.ts +483 -0
- package/src/client.ts +604 -0
- package/src/config.ts +404 -0
- package/src/credentials.ts +296 -0
- package/src/cucumber.ts +230 -0
- package/src/datafile.ts +481 -0
- package/src/db.ts +197 -0
- package/src/define.ts +285 -0
- package/src/describe.ts +282 -0
- package/src/drift.ts +259 -0
- package/src/env.ts +122 -0
- package/src/errors.ts +202 -0
- package/src/expectations.ts +191 -0
- package/src/fake.ts +156 -0
- package/src/fill.ts +24 -0
- package/src/host.ts +96 -0
- package/src/index.ts +143 -0
- package/src/ledger.ts +419 -0
- package/src/lint.ts +208 -0
- package/src/lockfile.ts +95 -0
- package/src/log.ts +82 -0
- package/src/op.ts +402 -0
- package/src/outline.ts +620 -0
- package/src/paths.ts +32 -0
- package/src/plugin.ts +37 -0
- package/src/policy.ts +139 -0
- package/src/resource.ts +670 -0
- package/src/resources.ts +311 -0
- package/src/run.ts +106 -0
- package/src/sandbox.ts +161 -0
- package/src/scenario.ts +1365 -0
- package/src/scenarios.ts +111 -0
- package/src/secret.ts +92 -0
- package/src/shared.ts +126 -0
- package/src/source.ts +82 -0
- package/src/steps.ts +90 -0
- package/src/version.ts +86 -0
- package/src/worker.ts +264 -0
- package/src/writer.ts +225 -0
package/dist/host.js
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
const LIFETIMES = ["long-lived", "wake-on-schedule"];
|
|
2
|
+
/** True when `schema` has a field at the dotted `path`, looking through optional, nullable and default wrappers. */
|
|
3
|
+
function hasPath(schema, path) {
|
|
4
|
+
let at = schema;
|
|
5
|
+
for (const key of path.split(".")) {
|
|
6
|
+
at = unwrap(at);
|
|
7
|
+
const shape = at.shape;
|
|
8
|
+
at = shape?.[key];
|
|
9
|
+
if (!at)
|
|
10
|
+
return false;
|
|
11
|
+
}
|
|
12
|
+
return true;
|
|
13
|
+
}
|
|
14
|
+
function unwrap(schema) {
|
|
15
|
+
let at = schema;
|
|
16
|
+
while ("unwrap" in at && typeof at.unwrap === "function")
|
|
17
|
+
at = at.unwrap();
|
|
18
|
+
return at;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Throws what is wrong with a resource type's `host`: an input or `revision` that names no field,
|
|
22
|
+
* `secrets` that is not write-only, a `revision` that is, an unknown lifetime, a type apply cannot
|
|
23
|
+
* change (not `writable`).
|
|
24
|
+
*/
|
|
25
|
+
export function checkHost(what, host, type) {
|
|
26
|
+
const problems = [];
|
|
27
|
+
if (!type.writable)
|
|
28
|
+
problems.push("it must be writable, so apply can create and update it");
|
|
29
|
+
if (!LIFETIMES.includes(host.capabilities?.lifetime)) {
|
|
30
|
+
problems.push(`capabilities.lifetime is one of ${LIFETIMES.join(", ")}`);
|
|
31
|
+
}
|
|
32
|
+
for (const [input, path] of [...Object.entries(host.inputs ?? {}), ["revision", host.revision]]) {
|
|
33
|
+
if (typeof path !== "string" || !hasPath(type.schema, path))
|
|
34
|
+
problems.push(`${input} names no field (${path})`);
|
|
35
|
+
}
|
|
36
|
+
if (!type.fields.writeOnly.includes(host.inputs?.secrets))
|
|
37
|
+
problems.push("inputs.secrets must be a write-only field");
|
|
38
|
+
if (type.fields.writeOnly.includes(host.revision))
|
|
39
|
+
problems.push("revision must not be write-only: drift reads it");
|
|
40
|
+
if (typeof host.outputs !== "function")
|
|
41
|
+
problems.push("outputs must be a function of the state");
|
|
42
|
+
if (problems.length)
|
|
43
|
+
throw new Error(`${what}: its host: ${problems.join("; ")}`);
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=host.js.map
|
package/dist/host.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"host.js","sourceRoot":"","sources":["../src/host.ts"],"names":[],"mappings":"AAqDA,MAAM,SAAS,GAA4B,CAAC,YAAY,EAAE,kBAAkB,CAAC,CAAC;AAE9E,oHAAoH;AACpH,SAAS,OAAO,CAAC,MAAiB,EAAE,IAAY;IAC9C,IAAI,EAAE,GAA0B,MAAM,CAAC;IACvC,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;QAClC,EAAE,GAAG,MAAM,CAAC,EAAE,CAAC,CAAC;QAChB,MAAM,KAAK,GAAI,EAA2B,CAAC,KAAK,CAAC;QACjD,EAAE,GAAG,KAAK,EAAE,CAAC,GAAG,CAAC,CAAC;QAClB,IAAI,CAAC,EAAE;YAAE,OAAO,KAAK,CAAC;IACxB,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,SAAS,MAAM,CAAC,MAAiB;IAC/B,IAAI,EAAE,GAAG,MAAM,CAAC;IAChB,OAAO,QAAQ,IAAI,EAAE,IAAI,OAAO,EAAE,CAAC,MAAM,KAAK,UAAU;QAAE,EAAE,GAAG,EAAE,CAAC,MAAM,EAAe,CAAC;IACxF,OAAO,EAAE,CAAC;AACZ,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,SAAS,CACvB,IAAY,EACZ,IAAc,EACd,IAA2F;IAE3F,MAAM,QAAQ,GAAa,EAAE,CAAC;IAC9B,IAAI,CAAC,IAAI,CAAC,QAAQ;QAAE,QAAQ,CAAC,IAAI,CAAC,wDAAwD,CAAC,CAAC;IAC5F,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,IAAI,CAAC,YAAY,EAAE,QAAQ,CAAC,EAAE,CAAC;QACrD,QAAQ,CAAC,IAAI,CAAC,mCAAmC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAC3E,CAAC;IACD,KAAK,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,IAAI,EAAE,CAAC,EAAE,CAAC,UAAU,EAAE,IAAI,CAAC,QAAQ,CAAU,CAAC,EAAE,CAAC;QACzG,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC;YAAE,QAAQ,CAAC,IAAI,CAAC,GAAG,KAAK,oBAAoB,IAAI,GAAG,CAAC,CAAC;IAClH,CAAC;IACD,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,EAAE,OAAO,CAAC;QAAE,QAAQ,CAAC,IAAI,CAAC,2CAA2C,CAAC,CAAC;IACtH,IAAI,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC;QAAE,QAAQ,CAAC,IAAI,CAAC,iDAAiD,CAAC,CAAC;IACpH,IAAI,OAAO,IAAI,CAAC,OAAO,KAAK,UAAU;QAAE,QAAQ,CAAC,IAAI,CAAC,yCAAyC,CAAC,CAAC;IACjG,IAAI,QAAQ,CAAC,MAAM;QAAE,MAAM,IAAI,KAAK,CAAC,GAAG,IAAI,eAAe,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AACpF,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export { type CallContext, type Connector, type ConnectorSetup, type ConnectorVendor, type Driver, type DriverEnv, type DriverFn, defineConnector, DriverError, type Effect, isOp, type Op, type OpSpec, type ResourceGroup, retryableStatus, type VendorInfo, withSetup, } from "./op.ts";
|
|
2
|
+
export { defineWorkflow, Principal, type ApprovalRequest, type ApprovalResult, type ApprovalState, type Approver, type Builtin, type Ctx, type SleepRequest, type StepOptions, type Use, type WorkflowDefinition, } from "./define.ts";
|
|
3
|
+
export { compareDeclared, type Declared, type Declaring, defineResource, diffDeclared, type FieldReference, type PlanAction, type References, type Resource, type ResourceFields, type ResourceOps, type ResourceSpec, withoutWriteOnly, } from "./resource.ts";
|
|
4
|
+
export { defineConfig, resolveConfig, resolveDatabaseUrl, type ResolvedConfig, type SanomaConfig } from "./config.ts";
|
|
5
|
+
export { type CredentialStatus, credentialsOf } from "./env.ts";
|
|
6
|
+
export { APPLY_WORKFLOW, ApplyInput, type ApplyPlan, type ApplyReport, type ApplyResult, type PlanAnswer, type PlanStep, } from "./apply.ts";
|
|
7
|
+
export { DRIFT_WORKFLOW, type DriftDeclared, type DriftField, type DriftReport, type DriftResult, type DriftStatus, } from "./drift.ts";
|
|
8
|
+
export type { DeclaredResource, ResourceProblem } from "./resources.ts";
|
|
9
|
+
export { allow, allowAll, approve, approvedFor, type Decision, definePolicy, deny, type Policy, type PolicyCall, type PolicyOp, policyOpOf, type RecordedDecision, } from "./policy.ts";
|
|
10
|
+
export { errorCode, type ErrorCode, type ErrorInfo, type InputIssue, invalidInput, PolicyDeniedError, RejectedError, SanomaError, } from "./errors.ts";
|
|
11
|
+
export { jsonlLedger, memoryLedger, postgresLedger, type LedgerBody, type LedgerGroup, type LedgerRecord, type LedgerStore, } from "./ledger.ts";
|
|
12
|
+
export { ApprovalMessage, APPROVALS_EVENT, decisionEventOf } from "./approvals.ts";
|
|
13
|
+
export { RUNTIME_VERSION } from "./version.ts";
|
|
14
|
+
export type { RunArgs } from "./run.ts";
|
|
15
|
+
export { startWorker, type Worker, type WorkerOptions } from "./worker.ts";
|
|
16
|
+
export { generateSecret, isSecret, memorySecrets, type Resolved, resolveSecrets, type Secret, type Secrets, } from "./secret.ts";
|
|
17
|
+
export { resolveRevision, SOURCE_RESOLVE, type Source } from "./source.ts";
|
|
18
|
+
export type { HostCapabilities, HostLifetime, HostOutputs, HostSpec } from "./host.ts";
|
|
19
|
+
export { SanomaClient, type RunsFilter, type RunSummary, type StartOptions, type StoredSecret } from "./client.ts";
|
|
20
|
+
export { dbosClient } from "./db.ts";
|
|
21
|
+
export { approverLabel, credentialReady, DEFAULT_SLEEP_NAME, ENDED_STATUSES, errorMessage, isEnded, isLoopback, mayDecide, problemAt, problemLine, RUN_STATUSES, type RunStatus, } from "./shared.ts";
|
|
22
|
+
export { defineDriver, type DriverImpl, type OpIdOf } from "./op.ts";
|
|
23
|
+
export { defineSteps, type Expect, type RunView, type Seed, type Steps } from "./steps.ts";
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export { defineConnector, DriverError, isOp, retryableStatus, withSetup, } from "./op.js";
|
|
2
|
+
export { defineWorkflow, Principal, } from "./define.js";
|
|
3
|
+
export { compareDeclared, defineResource, diffDeclared, withoutWriteOnly, } from "./resource.js";
|
|
4
|
+
export { defineConfig, resolveConfig, resolveDatabaseUrl } from "./config.js";
|
|
5
|
+
export { credentialsOf } from "./env.js";
|
|
6
|
+
export { APPLY_WORKFLOW, ApplyInput, } from "./apply.js";
|
|
7
|
+
export { DRIFT_WORKFLOW, } from "./drift.js";
|
|
8
|
+
export { allow, allowAll, approve, approvedFor, definePolicy, deny, policyOpOf, } from "./policy.js";
|
|
9
|
+
export { errorCode, invalidInput, PolicyDeniedError, RejectedError, SanomaError, } from "./errors.js";
|
|
10
|
+
export { jsonlLedger, memoryLedger, postgresLedger, } from "./ledger.js";
|
|
11
|
+
export { ApprovalMessage, APPROVALS_EVENT, decisionEventOf } from "./approvals.js";
|
|
12
|
+
export { RUNTIME_VERSION } from "./version.js";
|
|
13
|
+
export { startWorker } from "./worker.js";
|
|
14
|
+
// `secret` itself is at `@sanoma-ai/workflows/resources`, for data files; this is the drivers' side.
|
|
15
|
+
export { generateSecret, isSecret, memorySecrets, resolveSecrets, } from "./secret.js";
|
|
16
|
+
export { resolveRevision, SOURCE_RESOLVE } from "./source.js";
|
|
17
|
+
export { SanomaClient } from "./client.js";
|
|
18
|
+
// A raw DBOS client on the runtime's database, for tests and tools that read DBOS directly.
|
|
19
|
+
export { dbosClient } from "./db.js";
|
|
20
|
+
// Also at `@sanoma-ai/workflows/shared`, which a browser bundle can import.
|
|
21
|
+
export { approverLabel, credentialReady, DEFAULT_SLEEP_NAME, ENDED_STATUSES, errorMessage, isEnded, isLoopback, mayDecide, problemAt, problemLine, RUN_STATUSES, } from "./shared.js";
|
|
22
|
+
// describeConfig, with the outline and every problem lintWorkflow finds, lives at
|
|
23
|
+
// `@sanoma-ai/workflows/describe`: what a UI renders from, apart from what runs. The worker reads
|
|
24
|
+
// each workflow's outline too, to hold its runs to it.
|
|
25
|
+
export { defineDriver } from "./op.js";
|
|
26
|
+
// Steps for Gherkin tests, as data; Cucumber runs them from `@sanoma-ai/workflows/scenario`.
|
|
27
|
+
export { defineSteps } from "./steps.js";
|
|
28
|
+
// defineFake lives at `@sanoma-ai/workflows/fake`: test tooling, not runtime.
|
|
29
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAQL,eAAe,EACf,WAAW,EAEX,IAAI,EAIJ,eAAe,EAEf,SAAS,GACV,MAAM,SAAS,CAAC;AACjB,OAAO,EACL,cAAc,EACd,SAAS,GAWV,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,eAAe,EAGf,cAAc,EACd,YAAY,EAQZ,gBAAgB,GACjB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,kBAAkB,EAA0C,MAAM,aAAa,CAAC;AACtH,OAAO,EAAyB,aAAa,EAAE,MAAM,UAAU,CAAC;AAChE,OAAO,EACL,cAAc,EACd,UAAU,GAMX,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,cAAc,GAMf,MAAM,YAAY,CAAC;AAGpB,OAAO,EACL,KAAK,EACL,QAAQ,EACR,OAAO,EACP,WAAW,EAEX,YAAY,EACZ,IAAI,EAIJ,UAAU,GAEX,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,SAAS,EAIT,YAAY,EACZ,iBAAiB,EACjB,aAAa,EACb,WAAW,GACZ,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,WAAW,EACX,YAAY,EACZ,cAAc,GAKf,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACnF,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAE/C,OAAO,EAAE,WAAW,EAAmC,MAAM,aAAa,CAAC;AAC3E,qGAAqG;AACrG,OAAO,EACL,cAAc,EACd,QAAQ,EACR,aAAa,EAEb,cAAc,GAGf,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,eAAe,EAAE,cAAc,EAAe,MAAM,aAAa,CAAC;AAE3E,OAAO,EAAE,YAAY,EAA0E,MAAM,aAAa,CAAC;AACnH,4FAA4F;AAC5F,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,4EAA4E;AAC5E,OAAO,EACL,aAAa,EACb,eAAe,EACf,kBAAkB,EAClB,cAAc,EACd,YAAY,EACZ,OAAO,EACP,UAAU,EACV,SAAS,EACT,SAAS,EACT,WAAW,EACX,YAAY,GAEb,MAAM,aAAa,CAAC;AACrB,kFAAkF;AAClF,kGAAkG;AAClG,uDAAuD;AACvD,OAAO,EAAE,YAAY,EAAgC,MAAM,SAAS,CAAC;AACrE,6FAA6F;AAC7F,OAAO,EAAE,WAAW,EAAoD,MAAM,YAAY,CAAC;AAC3F,8EAA8E"}
|
package/dist/ledger.d.ts
ADDED
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
2
|
+
import type { Approver, Principal } from "./define.ts";
|
|
3
|
+
import { type ErrorInfo } from "./errors.ts";
|
|
4
|
+
import type { Effect } from "./op.ts";
|
|
5
|
+
import type { RecordedDecision } from "./policy.ts";
|
|
6
|
+
import type { Run } from "./run.ts";
|
|
7
|
+
/**
|
|
8
|
+
* The audit record of a run: who started it, every operation call with the policy's
|
|
9
|
+
* decision, every approval, and how it ended. Records are append-only.
|
|
10
|
+
*
|
|
11
|
+
* `id` is deterministic (`<runId>:<type>:<seq>`, `<runId>:<type>:<approvalId>` for an
|
|
12
|
+
* approval requested or decided, `<runId>:approval.refused:<approvalId>:refused:<n>` for
|
|
13
|
+
* the nth message an approval ignored) so that when DBOS replays a run after a restart,
|
|
14
|
+
* the records it writes again have the same ids and the store skips them. `seq` orders a
|
|
15
|
+
* run's records. `v` is the record format's version; `app` is the config's `appName`.
|
|
16
|
+
*/
|
|
17
|
+
export type LedgerRecord = {
|
|
18
|
+
v: 1;
|
|
19
|
+
app: string;
|
|
20
|
+
id: string;
|
|
21
|
+
runId: string;
|
|
22
|
+
seq: number;
|
|
23
|
+
at: number;
|
|
24
|
+
actor: Principal;
|
|
25
|
+
workflow: string;
|
|
26
|
+
/** Set on every record written while a `ctx.all` member runs; absent outside one. */
|
|
27
|
+
group?: LedgerGroup;
|
|
28
|
+
} & ({
|
|
29
|
+
type: "run.started";
|
|
30
|
+
input: unknown;
|
|
31
|
+
} | {
|
|
32
|
+
type: "op.called";
|
|
33
|
+
op: string;
|
|
34
|
+
/**
|
|
35
|
+
* The outline node of the call that made it (`OutlineNode.path`): where the step is in the
|
|
36
|
+
* code. Absent in records written before the worker placed calls (ledgers from 0.1).
|
|
37
|
+
*/
|
|
38
|
+
node?: string;
|
|
39
|
+
/**
|
|
40
|
+
* The step's name: the one the call gave (`{ name }`), or the operation's id. Absent in
|
|
41
|
+
* records written before steps had names.
|
|
42
|
+
*/
|
|
43
|
+
name?: string;
|
|
44
|
+
effect: Effect;
|
|
45
|
+
input: unknown;
|
|
46
|
+
/** The policy's decision, with its `reasons` and the policy's `policyVersion` when there are any. */
|
|
47
|
+
decision: RecordedDecision;
|
|
48
|
+
output?: unknown;
|
|
49
|
+
/**
|
|
50
|
+
* Why the call failed: `{ code?, name, message }`, and the vendor's `status`, `vendorCode`
|
|
51
|
+
* and `retryable` from a `DriverError`, or the `data` of one of the runtime's errors.
|
|
52
|
+
*/
|
|
53
|
+
error?: ErrorInfo;
|
|
54
|
+
durationMs: number;
|
|
55
|
+
/** Which try produced the output or the final error, from 1. Idempotent operations are retried. */
|
|
56
|
+
attempt?: number;
|
|
57
|
+
/** The approval that held the call, when its rejection stopped it. */
|
|
58
|
+
approval?: string;
|
|
59
|
+
} | {
|
|
60
|
+
type: "approval.requested";
|
|
61
|
+
approval: string;
|
|
62
|
+
/** The outline node of the `ctx.approval` call, or of the held operation's call for a policy request; as for `op.called`. */
|
|
63
|
+
node?: string;
|
|
64
|
+
/** The step's name: the title for a `ctx.approval`, the held call's name for a policy request; as for `op.called`. */
|
|
65
|
+
name?: string;
|
|
66
|
+
title: string;
|
|
67
|
+
approver: Approver;
|
|
68
|
+
requestedBy: "workflow" | "policy";
|
|
69
|
+
/** The operations the approval stands for, by id. */
|
|
70
|
+
covers: string[];
|
|
71
|
+
/** The operation call held, for a policy request. */
|
|
72
|
+
op?: string;
|
|
73
|
+
/** The held call's `seq`, which its `op.called` record has, for a policy request. */
|
|
74
|
+
opSeq?: number;
|
|
75
|
+
}
|
|
76
|
+
/** A message the approval ignored: from someone other than the approver, or not a decision. */
|
|
77
|
+
| {
|
|
78
|
+
type: "approval.refused";
|
|
79
|
+
approval: string;
|
|
80
|
+
by?: string;
|
|
81
|
+
reason: string;
|
|
82
|
+
} | {
|
|
83
|
+
type: "approval.decided";
|
|
84
|
+
approval: string;
|
|
85
|
+
decision: "approve" | "reject";
|
|
86
|
+
by: string;
|
|
87
|
+
note?: string;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* A `ctx.sleep` began; `until` is when it ends, in ms since the epoch: the time asked for, or
|
|
91
|
+
* for a duration the time the sleep started plus the duration, as the runtime saw it. Written
|
|
92
|
+
* for a time already past too, which waits not at all.
|
|
93
|
+
*/
|
|
94
|
+
| {
|
|
95
|
+
type: "sleep.started";
|
|
96
|
+
node?: string;
|
|
97
|
+
/** The step's name: the one the call gave (`{ name }`), or `Sleep`; as for `op.called`. */
|
|
98
|
+
name?: string;
|
|
99
|
+
until: number;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* A sandbox run seeded its fakes from the scenario named: each `Given` operation called
|
|
103
|
+
* through its fake, with what the fake returned, and each `Given` resource (by its type's id
|
|
104
|
+
* and its import id) put with its fields or removed (`gone`). Follows `run.started`.
|
|
105
|
+
*/
|
|
106
|
+
| {
|
|
107
|
+
type: "scenario.seeded";
|
|
108
|
+
scenario: string;
|
|
109
|
+
seeds: ({
|
|
110
|
+
op: string;
|
|
111
|
+
input: unknown;
|
|
112
|
+
output: unknown;
|
|
113
|
+
} | {
|
|
114
|
+
resource: string;
|
|
115
|
+
name: string;
|
|
116
|
+
fields: Record<string, unknown>;
|
|
117
|
+
} | {
|
|
118
|
+
resource: string;
|
|
119
|
+
name: string;
|
|
120
|
+
gone: true;
|
|
121
|
+
})[];
|
|
122
|
+
} | {
|
|
123
|
+
type: "run.finished";
|
|
124
|
+
output: unknown;
|
|
125
|
+
}
|
|
126
|
+
/** `error` as for `op.called`. */
|
|
127
|
+
| {
|
|
128
|
+
type: "run.failed";
|
|
129
|
+
error: ErrorInfo;
|
|
130
|
+
});
|
|
131
|
+
/**
|
|
132
|
+
* The `ctx.all` member a record was written in. `id` is `all:<seq>`, the run's next `seq` when
|
|
133
|
+
* the `ctx.all` began, so a replay names it the same; `index` is the member's position, `size`
|
|
134
|
+
* the member count and `node` the `ctx.all` call's outline node.
|
|
135
|
+
*/
|
|
136
|
+
export interface LedgerGroup {
|
|
137
|
+
id: string;
|
|
138
|
+
index: number;
|
|
139
|
+
size: number;
|
|
140
|
+
/** The outline node of the `ctx.all` call; as for `op.called`'s. */
|
|
141
|
+
node?: string;
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* The `ctx.all` member a call was made in. Each member runs inside `currentGroup.run`, and a
|
|
145
|
+
* queued call keeps the context it was queued in, so a call made outside the member meanwhile
|
|
146
|
+
* (one the workflow did not await) is not tagged as the member's.
|
|
147
|
+
*/
|
|
148
|
+
export declare const currentGroup: AsyncLocalStorage<LedgerGroup>;
|
|
149
|
+
export interface LedgerStore {
|
|
150
|
+
/**
|
|
151
|
+
* Adds a record, unless one with the same id is already stored. A failed append is tried
|
|
152
|
+
* again, up to three times. A store throws an error with `retryable: false` for a failure
|
|
153
|
+
* that must not be retried because it would only happen again, such as a corrupt file or a
|
|
154
|
+
* record that can't be written as JSON.
|
|
155
|
+
*/
|
|
156
|
+
append(record: LedgerRecord): Promise<void>;
|
|
157
|
+
/** A run's records in `seq` order. */
|
|
158
|
+
read(runId: string): Promise<LedgerRecord[]>;
|
|
159
|
+
/**
|
|
160
|
+
* Releases what the store holds (a connection pool), for good. Called by the one owner of the
|
|
161
|
+
* store: the worker's `stop` and `SanomaClient.close` close the Postgres store each made for a
|
|
162
|
+
* config without a `ledger` (`ownedLedger`); a store a config gives is its own to close.
|
|
163
|
+
*/
|
|
164
|
+
close?(): Promise<void>;
|
|
165
|
+
}
|
|
166
|
+
/** The store a worker or client runs on, and `close`, which ends it only when it was made for them. */
|
|
167
|
+
export interface OwnedLedger {
|
|
168
|
+
store: LedgerStore;
|
|
169
|
+
close(): Promise<void>;
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* The config's `ledger`, which stays the config's to close, or `postgresLedger` on its database,
|
|
173
|
+
* made here for the caller, and so the caller's to close: the worker and the client each make
|
|
174
|
+
* their own, and close it with themselves. Two of them on one database share the table, not a pool.
|
|
175
|
+
*/
|
|
176
|
+
export declare function ownedLedger(config: {
|
|
177
|
+
ledger?: LedgerStore;
|
|
178
|
+
databaseUrl: string;
|
|
179
|
+
}): OwnedLedger;
|
|
180
|
+
/**
|
|
181
|
+
* Keeps each run's records in `<dir>/<runId>.jsonl`, one JSON object per line. A run with no
|
|
182
|
+
* file, or a directory not yet created, reads as no records.
|
|
183
|
+
*
|
|
184
|
+
* A line with no newline at the end of a file is a write that was cut off (the process
|
|
185
|
+
* died mid-append): reads ignore it, and the next append removes it before writing.
|
|
186
|
+
* A line that isn't JSON anywhere else is corruption, and reads throw.
|
|
187
|
+
*/
|
|
188
|
+
export declare function jsonlLedger(dir: string): LedgerStore;
|
|
189
|
+
/**
|
|
190
|
+
* Keeps records in the `sanoma_ledger` table of the database at `databaseUrl`, beside DBOS's
|
|
191
|
+
* tables: the default store, which the worker, the app and the client share through Postgres.
|
|
192
|
+
* The database and the table are made on first use when they are not there (`prepareDatabase`).
|
|
193
|
+
* The pool is opened on first use and ended by `close()`, after which the store refuses use.
|
|
194
|
+
*/
|
|
195
|
+
export declare function postgresLedger(databaseUrl: string): LedgerStore & {
|
|
196
|
+
close(): Promise<void>;
|
|
197
|
+
};
|
|
198
|
+
/** Keeps records in memory, for tests. */
|
|
199
|
+
export declare function memoryLedger(): LedgerStore;
|
|
200
|
+
type Common = "v" | "app" | "id" | "runId" | "seq" | "at" | "actor" | "workflow" | "group";
|
|
201
|
+
/** What one kind of record says, without what every record carries: `type` and its own fields. */
|
|
202
|
+
export type LedgerBody = LedgerRecord extends infer R ? (R extends LedgerRecord ? Omit<R, Common> : never) : never;
|
|
203
|
+
/** A record of the run, with the next `seq` unless one is given, tagged with the `ctx.all` member it is written in. */
|
|
204
|
+
export declare function entry(run: Run, body: LedgerBody, opts?: {
|
|
205
|
+
seq?: number;
|
|
206
|
+
key?: string;
|
|
207
|
+
at?: number;
|
|
208
|
+
}): LedgerRecord;
|
|
209
|
+
export declare function write(run: Run, record: LedgerRecord): Promise<void>;
|
|
210
|
+
/**
|
|
211
|
+
* Records a failure. If the ledger fails too, throws both, so neither is lost, with the
|
|
212
|
+
* original's code: the run still fails the way it did.
|
|
213
|
+
*/
|
|
214
|
+
export declare function writeFailure(run: Run, record: LedgerRecord, original: unknown): Promise<void>;
|
|
215
|
+
/** Says that a record was deliberately not written, and why. */
|
|
216
|
+
export declare function skipped(record: LedgerRecord, why: string): void;
|
|
217
|
+
export {};
|
package/dist/ledger.js
ADDED
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
2
|
+
import { appendFile, mkdir, readFile, truncate } from "node:fs/promises";
|
|
3
|
+
import { join, resolve } from "node:path";
|
|
4
|
+
import { setTimeout as delay } from "node:timers/promises";
|
|
5
|
+
import { isDeepStrictEqual } from "node:util";
|
|
6
|
+
import { LEDGER_TABLE as TABLE, lazyPool, pgCode } from "./db.js";
|
|
7
|
+
import { isFinal, keepCode } from "./errors.js";
|
|
8
|
+
import { shown, warn } from "./log.js";
|
|
9
|
+
import { errorMessage } from "./shared.js";
|
|
10
|
+
/**
|
|
11
|
+
* The `ctx.all` member a call was made in. Each member runs inside `currentGroup.run`, and a
|
|
12
|
+
* queued call keeps the context it was queued in, so a call made outside the member meanwhile
|
|
13
|
+
* (one the workflow did not await) is not tagged as the member's.
|
|
14
|
+
*/
|
|
15
|
+
export const currentGroup = new AsyncLocalStorage();
|
|
16
|
+
/**
|
|
17
|
+
* The config's `ledger`, which stays the config's to close, or `postgresLedger` on its database,
|
|
18
|
+
* made here for the caller, and so the caller's to close: the worker and the client each make
|
|
19
|
+
* their own, and close it with themselves. Two of them on one database share the table, not a pool.
|
|
20
|
+
*/
|
|
21
|
+
export function ownedLedger(config) {
|
|
22
|
+
if (config.ledger)
|
|
23
|
+
return { store: config.ledger, close: async () => undefined };
|
|
24
|
+
const store = postgresLedger(config.databaseUrl);
|
|
25
|
+
return { store, close: () => store.close() };
|
|
26
|
+
}
|
|
27
|
+
const bySeq = (a, b) => a.seq - b.seq;
|
|
28
|
+
/** An error that trying again would only repeat. */
|
|
29
|
+
const final = (message, cause) => Object.assign(new Error(message, { cause }), { retryable: false });
|
|
30
|
+
/** JSON for a record, or an error that names the record when it can't be written as JSON (a BigInt, a cycle). */
|
|
31
|
+
function serialize(record) {
|
|
32
|
+
try {
|
|
33
|
+
return JSON.stringify(record);
|
|
34
|
+
}
|
|
35
|
+
catch (cause) {
|
|
36
|
+
const op = record.type === "op.called" ? ` (${record.op})` : "";
|
|
37
|
+
throw final(`Ledger record ${record.id}${op} can't be written as JSON: ${errorMessage(cause)}`, cause);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
/** The line `jsonlLedger` writes for a record: its JSON and a newline. */
|
|
41
|
+
const lineOf = (record) => `${serialize(record)}\n`;
|
|
42
|
+
const COMPARED = ["type", "input", "decision", "error", "output"];
|
|
43
|
+
/**
|
|
44
|
+
* The first record with an id stands. Says so when a later one with that id differs where it
|
|
45
|
+
* matters, showing both, so a replay that went another way can be told from a retry. Compared
|
|
46
|
+
* as values, not text: a store may give the kept record's keys back in another order (`jsonb`).
|
|
47
|
+
*/
|
|
48
|
+
function warnIfDifferent(stored, line) {
|
|
49
|
+
const later = JSON.parse(line);
|
|
50
|
+
const differs = COMPARED.flatMap((k) => {
|
|
51
|
+
const kept = stored[k];
|
|
52
|
+
const sent = later[k];
|
|
53
|
+
return isDeepStrictEqual(kept, sent) ? [] : [`${k} (kept ${shown(kept, 200)}, later ${shown(sent, 200)})`];
|
|
54
|
+
});
|
|
55
|
+
if (differs.length) {
|
|
56
|
+
warn(`ledger: kept the first record ${stored.id}; a later write differed in ${differs.join("; ")}`);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
/** The file's complete records, and the length of its complete lines when a cut-off line follows them. */
|
|
60
|
+
async function load(path) {
|
|
61
|
+
let text;
|
|
62
|
+
try {
|
|
63
|
+
text = await readFile(path, "utf8");
|
|
64
|
+
}
|
|
65
|
+
catch (err) {
|
|
66
|
+
if (err.code === "ENOENT")
|
|
67
|
+
return { records: [] };
|
|
68
|
+
throw err;
|
|
69
|
+
}
|
|
70
|
+
const complete = text.slice(0, text.lastIndexOf("\n") + 1);
|
|
71
|
+
const records = [];
|
|
72
|
+
complete.split("\n").forEach((line, i) => {
|
|
73
|
+
if (!line.trim())
|
|
74
|
+
return;
|
|
75
|
+
try {
|
|
76
|
+
records.push(JSON.parse(line));
|
|
77
|
+
}
|
|
78
|
+
catch (cause) {
|
|
79
|
+
throw final(`${path}:${i + 1}: corrupt ledger line`, cause);
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
return complete.length < text.length ? { records, tornAt: Buffer.byteLength(complete) } : { records };
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Keeps each run's records in `<dir>/<runId>.jsonl`, one JSON object per line. A run with no
|
|
86
|
+
* file, or a directory not yet created, reads as no records.
|
|
87
|
+
*
|
|
88
|
+
* A line with no newline at the end of a file is a write that was cut off (the process
|
|
89
|
+
* died mid-append): reads ignore it, and the next append removes it before writing.
|
|
90
|
+
* A line that isn't JSON anywhere else is corruption, and reads throw.
|
|
91
|
+
*/
|
|
92
|
+
export function jsonlLedger(dir) {
|
|
93
|
+
const root = resolve(dir);
|
|
94
|
+
const file = (runId) => join(root, `${encodeURIComponent(runId)}.jsonl`);
|
|
95
|
+
// Appends to one file are serialized, so the id check and the write can't interleave.
|
|
96
|
+
const queues = new Map();
|
|
97
|
+
return {
|
|
98
|
+
append(record) {
|
|
99
|
+
const path = file(record.runId);
|
|
100
|
+
const next = (queues.get(path) ?? Promise.resolve())
|
|
101
|
+
.catch(() => { })
|
|
102
|
+
.then(async () => {
|
|
103
|
+
const line = lineOf(record);
|
|
104
|
+
const { records, tornAt } = await load(path);
|
|
105
|
+
const stored = records.find((r) => r.id === record.id);
|
|
106
|
+
if (stored)
|
|
107
|
+
return warnIfDifferent(stored, line);
|
|
108
|
+
await mkdir(root, { recursive: true });
|
|
109
|
+
if (tornAt !== undefined) {
|
|
110
|
+
warn(`ledger: ${path} ended in a cut-off line; removed it before appending ${record.id}`);
|
|
111
|
+
await truncate(path, tornAt);
|
|
112
|
+
}
|
|
113
|
+
await appendFile(path, line);
|
|
114
|
+
});
|
|
115
|
+
queues.set(path, next);
|
|
116
|
+
const forget = () => {
|
|
117
|
+
if (queues.get(path) === next)
|
|
118
|
+
queues.delete(path);
|
|
119
|
+
};
|
|
120
|
+
next.then(forget, forget);
|
|
121
|
+
return next;
|
|
122
|
+
},
|
|
123
|
+
async read(runId) {
|
|
124
|
+
const path = file(runId);
|
|
125
|
+
await queues.get(path)?.catch(() => { });
|
|
126
|
+
// A missing directory, like a missing file, means nothing has been recorded there yet:
|
|
127
|
+
// `load` reads either as no records. Any other failure is thrown.
|
|
128
|
+
// A case-insensitive file system can map two run ids to one file.
|
|
129
|
+
return (await load(path)).records.filter((r) => r.runId === runId).toSorted(bySeq);
|
|
130
|
+
},
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
/** Postgres's class for a value it cannot store (a string with a NUL in `jsonb`): trying again would only repeat it. */
|
|
134
|
+
const DATA_EXCEPTION = /^22/;
|
|
135
|
+
/**
|
|
136
|
+
* Keeps records in the `sanoma_ledger` table of the database at `databaseUrl`, beside DBOS's
|
|
137
|
+
* tables: the default store, which the worker, the app and the client share through Postgres.
|
|
138
|
+
* The database and the table are made on first use when they are not there (`prepareDatabase`).
|
|
139
|
+
* The pool is opened on first use and ended by `close()`, after which the store refuses use.
|
|
140
|
+
*/
|
|
141
|
+
export function postgresLedger(databaseUrl) {
|
|
142
|
+
const pool = lazyPool(databaseUrl, "ledger");
|
|
143
|
+
return {
|
|
144
|
+
async append(record) {
|
|
145
|
+
const line = lineOf(record);
|
|
146
|
+
const conn = await pool.db();
|
|
147
|
+
const inserted = await conn
|
|
148
|
+
.query(`INSERT INTO ${TABLE} (id, run_id, seq, record) VALUES ($1, $2, $3, $4::jsonb) ON CONFLICT (id) DO NOTHING RETURNING n`, [record.id, record.runId, record.seq, line])
|
|
149
|
+
.catch((err) => {
|
|
150
|
+
if (DATA_EXCEPTION.test(pgCode(err))) {
|
|
151
|
+
throw final(`Ledger record ${record.id} can't be stored: ${errorMessage(err)}`, err);
|
|
152
|
+
}
|
|
153
|
+
throw err;
|
|
154
|
+
});
|
|
155
|
+
if (inserted.rows.length)
|
|
156
|
+
return;
|
|
157
|
+
// Another write of the id was first: the record it stored stands.
|
|
158
|
+
const { rows } = await conn.query(`SELECT record FROM ${TABLE} WHERE id = $1`, [
|
|
159
|
+
record.id,
|
|
160
|
+
]);
|
|
161
|
+
if (rows[0])
|
|
162
|
+
warnIfDifferent(rows[0].record, line);
|
|
163
|
+
},
|
|
164
|
+
async read(runId) {
|
|
165
|
+
const { rows } = await (await pool.db()).query(`SELECT record FROM ${TABLE} WHERE run_id = $1 ORDER BY seq, n`, [runId]);
|
|
166
|
+
// In seq order, as the other stores read; two records of one seq in write order.
|
|
167
|
+
return rows.map((r) => r.record);
|
|
168
|
+
},
|
|
169
|
+
close: pool.close,
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
/** Keeps records in memory, for tests. */
|
|
173
|
+
export function memoryLedger() {
|
|
174
|
+
const runs = new Map();
|
|
175
|
+
return {
|
|
176
|
+
async append(record) {
|
|
177
|
+
const line = serialize(record);
|
|
178
|
+
let run = runs.get(record.runId);
|
|
179
|
+
if (!run)
|
|
180
|
+
runs.set(record.runId, (run = new Map()));
|
|
181
|
+
const stored = run.get(record.id);
|
|
182
|
+
if (stored)
|
|
183
|
+
warnIfDifferent(stored, line);
|
|
184
|
+
else
|
|
185
|
+
run.set(record.id, JSON.parse(line));
|
|
186
|
+
},
|
|
187
|
+
async read(runId) {
|
|
188
|
+
return [...(runs.get(runId)?.values() ?? [])].map((r) => structuredClone(r)).toSorted(bySeq);
|
|
189
|
+
},
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
/** A record of the run, with the next `seq` unless one is given, tagged with the `ctx.all` member it is written in. */
|
|
193
|
+
export function entry(run, body, opts = {}) {
|
|
194
|
+
const seq = opts.seq ?? run.seq++;
|
|
195
|
+
const record = {
|
|
196
|
+
v: 1,
|
|
197
|
+
app: run.state.app,
|
|
198
|
+
id: `${run.id}:${body.type}:${opts.key ?? seq}`,
|
|
199
|
+
runId: run.id,
|
|
200
|
+
seq,
|
|
201
|
+
at: opts.at ?? Date.now(),
|
|
202
|
+
actor: run.actor,
|
|
203
|
+
workflow: run.workflow,
|
|
204
|
+
...body,
|
|
205
|
+
};
|
|
206
|
+
const group = currentGroup.getStore();
|
|
207
|
+
if (group)
|
|
208
|
+
record.group = group;
|
|
209
|
+
return record;
|
|
210
|
+
}
|
|
211
|
+
// Waits between tries of a failed append. Safe to repeat: append is idempotent by id.
|
|
212
|
+
const RETRY_DELAYS_MS = [50, 200, 800];
|
|
213
|
+
async function append(run, record) {
|
|
214
|
+
for (let i = 0;; i++) {
|
|
215
|
+
try {
|
|
216
|
+
return await run.state.ledger.append(record);
|
|
217
|
+
}
|
|
218
|
+
catch (err) {
|
|
219
|
+
const wait = RETRY_DELAYS_MS[i];
|
|
220
|
+
if (wait === undefined || isFinal(err))
|
|
221
|
+
throw err;
|
|
222
|
+
warn(`ledger: appending ${record.id} failed (${errorMessage(err)}); trying again in ${wait} ms`);
|
|
223
|
+
await delay(wait);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
/*
|
|
228
|
+
* Every ledger write happens outside DBOS steps. When DBOS replays a run after a restart,
|
|
229
|
+
* it re-runs the workflow function and these writes happen again with the same ids, so
|
|
230
|
+
* the store keeps one of each and fills in any the interrupted run never got to write.
|
|
231
|
+
*/
|
|
232
|
+
export async function write(run, record) {
|
|
233
|
+
await append(run, record);
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* Records a failure. If the ledger fails too, throws both, so neither is lost, with the
|
|
237
|
+
* original's code: the run still fails the way it did.
|
|
238
|
+
*/
|
|
239
|
+
export async function writeFailure(run, record, original) {
|
|
240
|
+
try {
|
|
241
|
+
await append(run, record);
|
|
242
|
+
}
|
|
243
|
+
catch (ledgerError) {
|
|
244
|
+
throw keepCode(new AggregateError([original, ledgerError], `${errorMessage(original)} (and the ledger could not record ${record.id}: ${errorMessage(ledgerError)})`, { cause: ledgerError }), original);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
/** Says that a record was deliberately not written, and why. */
|
|
248
|
+
export function skipped(record, why) {
|
|
249
|
+
warn(`did not write ${record.type} ${record.id}: ${why}`);
|
|
250
|
+
}
|
|
251
|
+
//# sourceMappingURL=ledger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ledger.js","sourceRoot":"","sources":["../src/ledger.ts"],"names":[],"mappings":"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"}
|
package/dist/lint.d.ts
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { type LintProblem, type SourceFile } from "./ast.ts";
|
|
2
|
+
export type { LintProblem } from "./ast.ts";
|
|
3
|
+
/**
|
|
4
|
+
* Checks a workflow or policy file. Imports allowed: the names in `WORKFLOW_IMPORTS` and any
|
|
5
|
+
* type from `@sanoma-ai/workflows` or `sanoma`, `@sanoma-ai/connector-<vendor>`, `zod`, and relative files. With
|
|
6
|
+
* `filename`, a relative import must stay inside the file's nearest `workflows/` or `policies/`
|
|
7
|
+
* directory, or its own directory when it has neither. No `instanceof` against the runtime's
|
|
8
|
+
* error classes: read `errorCode(err)`.
|
|
9
|
+
*
|
|
10
|
+
* In each workflow's `run` (the literal `defineWorkflow` is called with), it reports what the
|
|
11
|
+
* outline cannot draw, as the worker refuses to start with (`outlineBody`): `ctx` used other
|
|
12
|
+
* than called directly (passed on, aliased, destructured), a function defined in `run` that uses
|
|
13
|
+
* `ctx` where the outline does not read it, `ctx` in a `finally`, a step name that is no literal
|
|
14
|
+
* and two calls with one step name, and in `run` and the callbacks it reads, a `return` before
|
|
15
|
+
* the end, a `throw` outside a `catch`, a `break` out of a loop and `continue`. Each message
|
|
16
|
+
* names what to write instead.
|
|
17
|
+
*
|
|
18
|
+
* It guards against accidental non-determinism and accidental ways around the policy; it is
|
|
19
|
+
* not a sandbox, and code written to get around it can.
|
|
20
|
+
*/
|
|
21
|
+
export declare function lintWorkflow(source: string, filename?: string): LintProblem[];
|
|
22
|
+
/** The same, over a file `readSource` read: the reading `describeConfig` shares with the outline. */
|
|
23
|
+
export declare function lintWorkflow(file: SourceFile): LintProblem[];
|
|
24
|
+
/**
|
|
25
|
+
* Checks a data file under `resources/`: only named imports (a connector's constructors from its
|
|
26
|
+
* `…/resources` entry, and resources from other data files inside the same `resources/`
|
|
27
|
+
* directory), `export const <name> = <vendor>.<type>({ … })` with literal fields and names of
|
|
28
|
+
* declared resources, and `export default [ … ]`. Each message says what to write instead.
|
|
29
|
+
* It is the subset `readResources` reads (`readDataFile`), without the config: whether the
|
|
30
|
+
* connector, the type and the values are known is the reader's to say.
|
|
31
|
+
*/
|
|
32
|
+
export declare function lintResources(source: string, filename: string): LintProblem[];
|