@outputai/core 0.14.1-next.bd031f8.0 → 0.14.1-next.e9dfa49.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/credentials/credentials.d.ts +6 -0
- package/dist/credentials/credentials.js +87 -0
- package/dist/credentials/encrypted_yaml_provider.d.ts +2 -0
- package/dist/credentials/encrypted_yaml_provider.js +77 -0
- package/dist/credentials/encryption.d.ts +3 -0
- package/dist/credentials/encryption.js +6 -0
- package/dist/credentials/errors.d.ts +13 -0
- package/dist/credentials/errors.js +39 -0
- package/dist/credentials/index.d.ts +6 -0
- package/dist/credentials/index.js +5 -0
- package/dist/credentials/init.d.ts +1 -0
- package/dist/credentials/init.js +7 -0
- package/dist/credentials/paths.d.ts +7 -0
- package/dist/credentials/paths.js +14 -0
- package/dist/credentials/provider_registry.d.ts +3 -0
- package/dist/credentials/provider_registry.js +6 -0
- package/dist/credentials/types.d.ts +12 -0
- package/dist/credentials/types.js +1 -0
- package/package.json +25 -14
- package/src/async_storage.d.ts +4 -0
- package/src/consts.js +0 -2
- package/src/helpers/object.d.ts +2 -0
- package/src/helpers/search_attributes.js +25 -0
- package/src/helpers/temporal_context.js +4 -1
- package/src/hooks/index.d.ts +6 -8
- package/src/hooks/index.js +0 -3
- package/src/interface/workflow.js +10 -2
- package/src/logger/index.d.ts +4 -0
- package/src/worker/index.js +3 -1
- package/bin/healthcheck.spec.js +0 -90
- package/src/bus.spec.js +0 -196
- package/src/errors.spec.js +0 -19
- package/src/helpers/activity_options.spec.js +0 -55
- package/src/helpers/component.spec.js +0 -54
- package/src/helpers/error_serializer.spec.js +0 -479
- package/src/helpers/errors.spec.js +0 -189
- package/src/helpers/fetch.spec.js +0 -292
- package/src/helpers/function.spec.js +0 -24
- package/src/helpers/object.spec.js +0 -425
- package/src/helpers/promise.spec.js +0 -62
- package/src/helpers/redact.spec.js +0 -66
- package/src/helpers/string.spec.js +0 -99
- package/src/helpers/temporal_context.spec.ts +0 -83
- package/src/helpers/trace_info.spec.js +0 -45
- package/src/helpers/workflow_context.spec.js +0 -46
- package/src/hooks/index.spec.js +0 -265
- package/src/hooks/pending_hooks.spec.js +0 -62
- package/src/interface/evaluator.spec.js +0 -685
- package/src/interface/logger.spec.js +0 -172
- package/src/interface/step.spec.js +0 -128
- package/src/interface/validations/index.spec.js +0 -197
- package/src/interface/validations/schemas.spec.js +0 -229
- package/src/interface/webhook.spec.js +0 -170
- package/src/interface/workflow.spec.js +0 -710
- package/src/interface/workflow_utils.spec.js +0 -271
- package/src/interface/zod_integration.spec.js +0 -646
- package/src/internal_activities/index.spec.js +0 -186
- package/src/logger/development.spec.js +0 -87
- package/src/logger/index.spec.js +0 -27
- package/src/logger/production.spec.js +0 -71
- package/src/sdk/helpers/component_metadata.spec.js +0 -30
- package/src/sdk/helpers/objects.spec.js +0 -16
- package/src/sdk/helpers/path.spec.js +0 -102
- package/src/sdk/runtime/context.spec.js +0 -37
- package/src/sdk/runtime/events.spec.js +0 -40
- package/src/temporal/client.spec.js +0 -124
- package/src/tracing/processors/local/index.spec.js +0 -204
- package/src/tracing/processors/s3/configs.spec.js +0 -64
- package/src/tracing/processors/s3/index.spec.js +0 -185
- package/src/tracing/processors/s3/redis_client.spec.js +0 -179
- package/src/tracing/processors/s3/s3_client.spec.js +0 -68
- package/src/tracing/tools/build_trace_tree.spec.js +0 -216
- package/src/tracing/trace_engine.spec.js +0 -299
- package/src/worker/bundle.spec.js +0 -53
- package/src/worker/bundler_options.spec.js +0 -62
- package/src/worker/catalog_workflow/catalog_publisher.spec.js +0 -317
- package/src/worker/catalog_workflow/index.spec.js +0 -222
- package/src/worker/configs.spec.js +0 -213
- package/src/worker/configs_tuner_schema.spec.js +0 -127
- package/src/worker/connection_monitor.spec.js +0 -328
- package/src/worker/global_functions.spec.js +0 -55
- package/src/worker/index.spec.js +0 -526
- package/src/worker/interceptors/activity.spec.js +0 -369
- package/src/worker/interceptors/headers.spec.js +0 -73
- package/src/worker/interceptors/workflow.spec.js +0 -357
- package/src/worker/interruption.spec.js +0 -177
- package/src/worker/loader/activities.spec.js +0 -212
- package/src/worker/loader/hooks.spec.js +0 -64
- package/src/worker/loader/matchers.spec.js +0 -140
- package/src/worker/loader/tools.spec.js +0 -595
- package/src/worker/loader/workflows.spec.js +0 -287
- package/src/worker/log_hooks.spec.js +0 -303
- package/src/worker/shutdown.spec.js +0 -197
- package/src/worker/sinks.spec.js +0 -220
- package/src/worker/telemetry.spec.js +0 -80
- package/src/worker/temporal_logger.spec.js +0 -90
- package/src/worker/webpack_loaders/tools.spec.js +0 -314
- package/src/worker/webpack_loaders/workflow_rewriter/collect_target_imports.spec.js +0 -134
- package/src/worker/webpack_loaders/workflow_rewriter/index.spec.js +0 -154
- package/src/worker/webpack_loaders/workflow_rewriter/rewrite_activity_calls.spec.js +0 -165
- package/src/worker/webpack_loaders/workflow_validator/index.spec.js +0 -202
- package/src/worker/webpack_loaders/workflow_validator/npm_workflow_export_resolve.spec.js +0 -374
- package/src/worker/worker_runner.spec.js +0 -220
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { dirname } from 'node:path';
|
|
2
|
+
import { MissingCredentialError } from './errors.js';
|
|
3
|
+
import { deepMerge } from '#helpers/object';
|
|
4
|
+
import { getProvider } from './provider_registry.js';
|
|
5
|
+
import { Storage } from '#async_storage';
|
|
6
|
+
const getNestedValue = (obj, dotPath) => dotPath.split('.').reduce((acc, part) => acc?.[part], obj);
|
|
7
|
+
const detectEnvironment = () => {
|
|
8
|
+
const env = process.env.NODE_ENV;
|
|
9
|
+
return (env === 'production' || env === 'development') ? env : undefined;
|
|
10
|
+
};
|
|
11
|
+
const GLOBAL_CACHE_KEY = Symbol('global');
|
|
12
|
+
const cache = new Map();
|
|
13
|
+
const loadGlobal = () => {
|
|
14
|
+
if (cache.has(GLOBAL_CACHE_KEY)) {
|
|
15
|
+
return cache.get(GLOBAL_CACHE_KEY);
|
|
16
|
+
}
|
|
17
|
+
const data = getProvider().loadGlobal({ environment: detectEnvironment() });
|
|
18
|
+
cache.set(GLOBAL_CACHE_KEY, data);
|
|
19
|
+
return data;
|
|
20
|
+
};
|
|
21
|
+
const loadForWorkflow = (workflowName, workflowDir) => {
|
|
22
|
+
if (cache.has(workflowName)) {
|
|
23
|
+
return cache.get(workflowName);
|
|
24
|
+
}
|
|
25
|
+
const globalData = loadGlobal();
|
|
26
|
+
const workflowData = getProvider().loadForWorkflow({
|
|
27
|
+
workflowName,
|
|
28
|
+
workflowDir,
|
|
29
|
+
environment: detectEnvironment()
|
|
30
|
+
});
|
|
31
|
+
const merged = workflowData ? deepMerge(globalData, workflowData) : globalData;
|
|
32
|
+
cache.set(workflowName, merged);
|
|
33
|
+
return merged;
|
|
34
|
+
};
|
|
35
|
+
const getWorkflowContext = () => {
|
|
36
|
+
const ctx = Storage.load();
|
|
37
|
+
if (!ctx) {
|
|
38
|
+
return { workflowName: undefined, workflowDir: undefined };
|
|
39
|
+
}
|
|
40
|
+
return {
|
|
41
|
+
workflowName: ctx.activityInfo.workflowType,
|
|
42
|
+
workflowDir: dirname(ctx.workflowFilename)
|
|
43
|
+
};
|
|
44
|
+
};
|
|
45
|
+
const load = () => {
|
|
46
|
+
const { workflowName, workflowDir } = getWorkflowContext();
|
|
47
|
+
if (!workflowName) {
|
|
48
|
+
return loadGlobal();
|
|
49
|
+
}
|
|
50
|
+
return loadForWorkflow(workflowName, workflowDir);
|
|
51
|
+
};
|
|
52
|
+
const CREDENTIAL_REF_PREFIX = 'credential:';
|
|
53
|
+
export const resolveCredentialRefs = () => {
|
|
54
|
+
const refs = Object.entries(process.env)
|
|
55
|
+
.filter(([, v]) => typeof v === 'string' && v.startsWith(CREDENTIAL_REF_PREFIX));
|
|
56
|
+
if (refs.length === 0) {
|
|
57
|
+
return [];
|
|
58
|
+
}
|
|
59
|
+
const data = loadGlobal();
|
|
60
|
+
const resolved = [];
|
|
61
|
+
for (const [envVar, value] of refs) {
|
|
62
|
+
const credPath = value.slice(CREDENTIAL_REF_PREFIX.length);
|
|
63
|
+
const credValue = getNestedValue(data, credPath);
|
|
64
|
+
if (typeof credValue === 'string' && credValue.length > 0) {
|
|
65
|
+
process.env[envVar] = credValue;
|
|
66
|
+
resolved.push(envVar);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return resolved;
|
|
70
|
+
};
|
|
71
|
+
export const credentials = {
|
|
72
|
+
get: (path, defaultValue = undefined) => {
|
|
73
|
+
const data = load();
|
|
74
|
+
const value = getNestedValue(data, path);
|
|
75
|
+
return value !== undefined ? value : defaultValue;
|
|
76
|
+
},
|
|
77
|
+
require: (path) => {
|
|
78
|
+
const value = credentials.get(path);
|
|
79
|
+
if (value === undefined || value === null) {
|
|
80
|
+
throw new MissingCredentialError(path);
|
|
81
|
+
}
|
|
82
|
+
return value;
|
|
83
|
+
},
|
|
84
|
+
_reset: () => {
|
|
85
|
+
cache.clear();
|
|
86
|
+
}
|
|
87
|
+
};
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { readFileSync, existsSync } from 'node:fs';
|
|
2
|
+
import { load as parseYaml } from 'js-yaml';
|
|
3
|
+
import { decrypt } from './encryption.js';
|
|
4
|
+
import { InvalidCredentialsKeyError, MalformedCredentialsKeyError, MissingKeyError } from './errors.js';
|
|
5
|
+
import { resolveKeyEnvVar, resolveCredentialsPath, resolveKeyPath, resolveWorkflowCredentialsPath, resolveWorkflowKeyPath, resolveWorkflowKeyEnvVar } from './paths.js';
|
|
6
|
+
const resolveBaseDir = () => {
|
|
7
|
+
const arg = process.argv[2];
|
|
8
|
+
return (arg && arg.startsWith('/')) ? arg : process.cwd();
|
|
9
|
+
};
|
|
10
|
+
const resolveKey = (environment) => {
|
|
11
|
+
const envVarName = resolveKeyEnvVar(environment);
|
|
12
|
+
if (process.env[envVarName]) {
|
|
13
|
+
return process.env[envVarName];
|
|
14
|
+
}
|
|
15
|
+
const keyFilePath = resolveKeyPath(resolveBaseDir(), environment);
|
|
16
|
+
if (existsSync(keyFilePath)) {
|
|
17
|
+
return readFileSync(keyFilePath, 'utf8').trim();
|
|
18
|
+
}
|
|
19
|
+
throw new MissingKeyError(environment);
|
|
20
|
+
};
|
|
21
|
+
const resolveWorkflowKey = (workflowName, workflowDir) => {
|
|
22
|
+
const envVarName = resolveWorkflowKeyEnvVar(workflowName);
|
|
23
|
+
if (process.env[envVarName]) {
|
|
24
|
+
return process.env[envVarName];
|
|
25
|
+
}
|
|
26
|
+
const keyPath = resolveWorkflowKeyPath(workflowDir);
|
|
27
|
+
if (existsSync(keyPath)) {
|
|
28
|
+
return readFileSync(keyPath, 'utf8').trim();
|
|
29
|
+
}
|
|
30
|
+
return resolveKey(undefined);
|
|
31
|
+
};
|
|
32
|
+
const safeDecrypt = (ciphertext, key, credPath) => {
|
|
33
|
+
try {
|
|
34
|
+
return decrypt(ciphertext, key);
|
|
35
|
+
}
|
|
36
|
+
catch (error) {
|
|
37
|
+
if (error instanceof RangeError) {
|
|
38
|
+
throw new MalformedCredentialsKeyError(credPath, error.message);
|
|
39
|
+
}
|
|
40
|
+
if (error instanceof Error && error.message.includes('aes/gcm')) {
|
|
41
|
+
throw new InvalidCredentialsKeyError(credPath);
|
|
42
|
+
}
|
|
43
|
+
throw new InvalidCredentialsKeyError(credPath, error instanceof Error ? error.message : String(error));
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
const decryptYaml = (credPath, key) => {
|
|
47
|
+
const ciphertext = readFileSync(credPath, 'utf8').trim();
|
|
48
|
+
const plaintext = safeDecrypt(ciphertext, key, credPath);
|
|
49
|
+
return parseYaml(plaintext) || {};
|
|
50
|
+
};
|
|
51
|
+
export const encryptedYamlProvider = {
|
|
52
|
+
loadGlobal: ({ environment }) => {
|
|
53
|
+
const baseDir = resolveBaseDir();
|
|
54
|
+
const credPath = resolveCredentialsPath(baseDir, environment);
|
|
55
|
+
if (!existsSync(credPath)) {
|
|
56
|
+
if (environment) {
|
|
57
|
+
const defaultPath = resolveCredentialsPath(baseDir, undefined);
|
|
58
|
+
if (existsSync(defaultPath)) {
|
|
59
|
+
return decryptYaml(defaultPath, resolveKey(undefined));
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return {};
|
|
63
|
+
}
|
|
64
|
+
return decryptYaml(credPath, resolveKey(environment));
|
|
65
|
+
},
|
|
66
|
+
loadForWorkflow: ({ workflowName, workflowDir }) => {
|
|
67
|
+
if (!workflowDir) {
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
const credPath = resolveWorkflowCredentialsPath(workflowDir);
|
|
71
|
+
if (!existsSync(credPath)) {
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
const key = resolveWorkflowKey(workflowName, workflowDir);
|
|
75
|
+
return decryptYaml(credPath, key);
|
|
76
|
+
}
|
|
77
|
+
};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { gcm } from '@noble/ciphers/aes.js';
|
|
2
|
+
import { managedNonce, randomBytes, hexToBytes, bytesToHex } from '@noble/ciphers/utils.js';
|
|
3
|
+
const aes = managedNonce(gcm);
|
|
4
|
+
export const generateKey = () => bytesToHex(randomBytes(32));
|
|
5
|
+
export const encrypt = (plaintext, keyHex) => Buffer.from(aes(hexToBytes(keyHex)).encrypt(new TextEncoder().encode(plaintext))).toString('base64');
|
|
6
|
+
export const decrypt = (ciphertext, keyHex) => new TextDecoder().decode(aes(hexToBytes(keyHex)).decrypt(Buffer.from(ciphertext, 'base64')));
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { FatalError } from '#errors';
|
|
2
|
+
export declare class MissingKeyError extends FatalError {
|
|
3
|
+
constructor(environment?: string | null);
|
|
4
|
+
}
|
|
5
|
+
export declare class MissingCredentialError extends FatalError {
|
|
6
|
+
constructor(path: string);
|
|
7
|
+
}
|
|
8
|
+
export declare class InvalidCredentialsKeyError extends FatalError {
|
|
9
|
+
constructor(credentialsPath: string, underlyingError?: string);
|
|
10
|
+
}
|
|
11
|
+
export declare class MalformedCredentialsKeyError extends FatalError {
|
|
12
|
+
constructor(credentialsPath: string, detail: string);
|
|
13
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { FatalError } from '#errors';
|
|
2
|
+
export class MissingKeyError extends FatalError {
|
|
3
|
+
constructor(environment) {
|
|
4
|
+
const envVar = environment ?
|
|
5
|
+
`OUTPUT_CREDENTIALS_KEY_${environment.toUpperCase()}` :
|
|
6
|
+
'OUTPUT_CREDENTIALS_KEY';
|
|
7
|
+
const keyFile = environment ?
|
|
8
|
+
`config/credentials/${environment}.key` :
|
|
9
|
+
'config/credentials.key';
|
|
10
|
+
super(`No credentials key found. Set ${envVar} env var or create ${keyFile}.`);
|
|
11
|
+
this.name = 'MissingKeyError';
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
export class MissingCredentialError extends FatalError {
|
|
15
|
+
constructor(path) {
|
|
16
|
+
super(`Required credential not found: "${path}".`);
|
|
17
|
+
this.name = 'MissingCredentialError';
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
export class InvalidCredentialsKeyError extends FatalError {
|
|
21
|
+
constructor(credentialsPath, underlyingError) {
|
|
22
|
+
const message = underlyingError ?
|
|
23
|
+
`Failed to decrypt ${credentialsPath}: ${underlyingError}. ` +
|
|
24
|
+
'The credentials key may not match the one used to encrypt this file, or the credentials file may be corrupted. ' +
|
|
25
|
+
'Check OUTPUT_CREDENTIALS_KEY env var or config/credentials.key.' :
|
|
26
|
+
`Failed to decrypt ${credentialsPath}. The credentials key does not match the one used to encrypt this file. ` +
|
|
27
|
+
'Check OUTPUT_CREDENTIALS_KEY env var or config/credentials.key.';
|
|
28
|
+
super(message);
|
|
29
|
+
this.name = 'InvalidCredentialsKeyError';
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
export class MalformedCredentialsKeyError extends FatalError {
|
|
33
|
+
constructor(credentialsPath, detail) {
|
|
34
|
+
super(`Credentials key for ${credentialsPath} is malformed (${detail}). ` +
|
|
35
|
+
'The key must be exactly 64 hex characters. ' +
|
|
36
|
+
'Check OUTPUT_CREDENTIALS_KEY env var or config/credentials.key for typos, whitespace, or truncation.');
|
|
37
|
+
this.name = 'MalformedCredentialsKeyError';
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { credentials, resolveCredentialRefs } from './credentials.js';
|
|
2
|
+
export { setProvider } from './provider_registry.js';
|
|
3
|
+
export { encrypt, decrypt, generateKey } from './encryption.js';
|
|
4
|
+
export { InvalidCredentialsKeyError, MalformedCredentialsKeyError, MissingCredentialError, MissingKeyError } from './errors.js';
|
|
5
|
+
export { getNestedValue, resolveCredentialsPath, resolveKeyPath, resolveKeyEnvVar, resolveWorkflowCredentialsPath, resolveWorkflowKeyPath, resolveWorkflowKeyEnvVar } from './paths.js';
|
|
6
|
+
export type { CredentialsProvider } from './types.js';
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { credentials, resolveCredentialRefs } from './credentials.js';
|
|
2
|
+
export { setProvider } from './provider_registry.js';
|
|
3
|
+
export { encrypt, decrypt, generateKey } from './encryption.js';
|
|
4
|
+
export { InvalidCredentialsKeyError, MalformedCredentialsKeyError, MissingCredentialError, MissingKeyError } from './errors.js';
|
|
5
|
+
export { getNestedValue, resolveCredentialsPath, resolveKeyPath, resolveKeyEnvVar, resolveWorkflowCredentialsPath, resolveWorkflowKeyPath, resolveWorkflowKeyEnvVar } from './paths.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const init: () => void;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { createChildLogger } from '#logger';
|
|
2
|
+
import { resolveCredentialRefs } from './credentials.js';
|
|
3
|
+
const log = createChildLogger('Credentials');
|
|
4
|
+
export const init = () => {
|
|
5
|
+
const vars = resolveCredentialRefs();
|
|
6
|
+
log.info('Resolved credential env vars', { vars });
|
|
7
|
+
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export declare const resolveKeyEnvVar: (environment?: string) => string;
|
|
2
|
+
export declare const resolveWorkflowKeyEnvVar: (workflowName: string) => string;
|
|
3
|
+
export declare const resolveCredentialsPath: (baseDir: string, environment?: string) => string;
|
|
4
|
+
export declare const resolveKeyPath: (baseDir: string, environment?: string) => string;
|
|
5
|
+
export declare const resolveWorkflowCredentialsPath: (workflowDir: string) => string;
|
|
6
|
+
export declare const resolveWorkflowKeyPath: (workflowDir: string) => string;
|
|
7
|
+
export declare const getNestedValue: (obj: unknown, dotPath: string) => unknown;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { resolve } from 'node:path';
|
|
2
|
+
export const resolveKeyEnvVar = (environment) => environment ?
|
|
3
|
+
`OUTPUT_CREDENTIALS_KEY_${environment.toUpperCase()}` :
|
|
4
|
+
'OUTPUT_CREDENTIALS_KEY';
|
|
5
|
+
export const resolveWorkflowKeyEnvVar = (workflowName) => `OUTPUT_CREDENTIALS_KEY_${workflowName.toUpperCase()}`;
|
|
6
|
+
export const resolveCredentialsPath = (baseDir, environment) => environment ?
|
|
7
|
+
resolve(baseDir, `config/credentials/${environment}.yml.enc`) :
|
|
8
|
+
resolve(baseDir, 'config/credentials.yml.enc');
|
|
9
|
+
export const resolveKeyPath = (baseDir, environment) => environment ?
|
|
10
|
+
resolve(baseDir, `config/credentials/${environment}.key`) :
|
|
11
|
+
resolve(baseDir, 'config/credentials.key');
|
|
12
|
+
export const resolveWorkflowCredentialsPath = (workflowDir) => resolve(workflowDir, 'credentials.yml.enc');
|
|
13
|
+
export const resolveWorkflowKeyPath = (workflowDir) => resolve(workflowDir, 'credentials.key');
|
|
14
|
+
export const getNestedValue = (obj, dotPath) => dotPath.split('.').reduce((acc, part) => acc && typeof acc === 'object' ? acc[part] : undefined, obj);
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export interface GlobalContext {
|
|
2
|
+
environment: string | undefined;
|
|
3
|
+
}
|
|
4
|
+
export interface WorkflowContext {
|
|
5
|
+
workflowName: string;
|
|
6
|
+
workflowDir: string | undefined;
|
|
7
|
+
environment?: string | undefined;
|
|
8
|
+
}
|
|
9
|
+
export interface CredentialsProvider {
|
|
10
|
+
loadGlobal(context: GlobalContext): Record<string, unknown>;
|
|
11
|
+
loadForWorkflow(context: WorkflowContext): Record<string, unknown> | null;
|
|
12
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@outputai/core",
|
|
3
|
-
"version": "0.14.1-next.
|
|
3
|
+
"version": "0.14.1-next.e9dfa49.0",
|
|
4
4
|
"description": "The core module of the output framework",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|
|
@@ -26,11 +26,18 @@
|
|
|
26
26
|
"./temporal": {
|
|
27
27
|
"types": "./src/temporal/index.d.ts",
|
|
28
28
|
"import": "./src/temporal/index.js"
|
|
29
|
+
},
|
|
30
|
+
"./credentials": {
|
|
31
|
+
"types": "./dist/credentials/index.d.ts",
|
|
32
|
+
"import": "./dist/credentials/index.js"
|
|
29
33
|
}
|
|
30
34
|
},
|
|
31
35
|
"files": [
|
|
32
36
|
"./src",
|
|
33
|
-
"./
|
|
37
|
+
"./dist",
|
|
38
|
+
"./bin",
|
|
39
|
+
"!**/*.spec.*",
|
|
40
|
+
"!src/credentials"
|
|
34
41
|
],
|
|
35
42
|
"bin": {
|
|
36
43
|
"output-worker": "./bin/worker.sh",
|
|
@@ -39,29 +46,32 @@
|
|
|
39
46
|
"output-copy-assets": "./bin/copy_assets.js"
|
|
40
47
|
},
|
|
41
48
|
"dependencies": {
|
|
42
|
-
"@aws-sdk/client-s3": "3.
|
|
43
|
-
"@aws-sdk/lib-storage": "3.
|
|
49
|
+
"@aws-sdk/client-s3": "3.1138.0",
|
|
50
|
+
"@aws-sdk/lib-storage": "3.1138.0",
|
|
44
51
|
"@babel/generator": "7.29.1",
|
|
45
|
-
"@babel/parser": "7.29.
|
|
52
|
+
"@babel/parser": "7.29.9",
|
|
46
53
|
"@babel/traverse": "7.29.0",
|
|
47
54
|
"@babel/types": "7.29.0",
|
|
48
|
-
"@
|
|
49
|
-
"@temporalio/
|
|
50
|
-
"@temporalio/
|
|
51
|
-
"@temporalio/
|
|
52
|
-
"@temporalio/
|
|
55
|
+
"@noble/ciphers": "2.4.0",
|
|
56
|
+
"@temporalio/activity": "1.24.0",
|
|
57
|
+
"@temporalio/client": "1.24.0",
|
|
58
|
+
"@temporalio/common": "1.24.0",
|
|
59
|
+
"@temporalio/worker": "1.24.0",
|
|
60
|
+
"@temporalio/workflow": "1.24.0",
|
|
53
61
|
"folder-hash": "4.1.3",
|
|
54
|
-
"
|
|
62
|
+
"js-yaml": "5.4.2",
|
|
63
|
+
"json-stream-stringify": "3.1.7",
|
|
55
64
|
"redis": "5.12.1",
|
|
56
65
|
"stacktrace-parser": "0.1.11",
|
|
57
|
-
"undici": "8.
|
|
66
|
+
"undici": "8.11.0",
|
|
58
67
|
"winston": "3.19.0"
|
|
59
68
|
},
|
|
60
69
|
"peerDependencies": {
|
|
61
70
|
"zod": ">=4.3 <5"
|
|
62
71
|
},
|
|
63
72
|
"devDependencies": {
|
|
64
|
-
"
|
|
73
|
+
"@types/js-yaml": "4.0.9",
|
|
74
|
+
"zod": "4.6.5"
|
|
65
75
|
},
|
|
66
76
|
"license": "Apache-2.0",
|
|
67
77
|
"publishConfig": {
|
|
@@ -70,6 +80,7 @@
|
|
|
70
80
|
"imports": {
|
|
71
81
|
"#bus": "./src/bus.js",
|
|
72
82
|
"#consts": "./src/consts.js",
|
|
83
|
+
"#credentials/*": "./dist/credentials/*.js",
|
|
73
84
|
"#errors": "./src/errors.js",
|
|
74
85
|
"#logger": "./src/logger/index.js",
|
|
75
86
|
"#hooks/*": "./src/hooks/*.js",
|
|
@@ -82,6 +93,6 @@
|
|
|
82
93
|
},
|
|
83
94
|
"scripts": {
|
|
84
95
|
"worker": "node ./src/worker/index.js",
|
|
85
|
-
"build": "pnpm exec tsc -p ./tsconfig.typecheck.json"
|
|
96
|
+
"build": "rm -rf ./dist && tsc && pnpm exec tsc -p ./tsconfig.typecheck.json"
|
|
86
97
|
}
|
|
87
98
|
}
|
package/src/consts.js
CHANGED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
const serializeValue = value => typeof value?.toISOString === 'function' ? value.toISOString() : value;
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Converts Datetime (Date) search attribute values to ISO strings so the result survives JSON and structured-clone boundaries identically.
|
|
5
|
+
*
|
|
6
|
+
* @param {Record<string, unknown[]>} [searchAttributes] Temporal untyped search attributes
|
|
7
|
+
* @returns {Record<string, Array<string|number|boolean>>|undefined} Undefined when there are no attributes
|
|
8
|
+
*/
|
|
9
|
+
export const serializeSearchAttributes = searchAttributes =>
|
|
10
|
+
Object.keys( searchAttributes ?? {} ).length ?
|
|
11
|
+
Object.fromEntries( Object.entries( searchAttributes ).map( ( [ k, values ] ) => [ k, values.map( serializeValue ) ] ) ) :
|
|
12
|
+
undefined;
|
|
13
|
+
|
|
14
|
+
const isSystemAttribute = name => name.startsWith( 'Temporal' ) || name === 'BuildIds';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Drops Temporal system search attributes so the rest can be forwarded to child workflows.
|
|
18
|
+
*
|
|
19
|
+
* @param {Record<string, unknown[]>} [searchAttributes] Temporal untyped search attributes
|
|
20
|
+
* @returns {Record<string, unknown[]>|undefined} Undefined when nothing remains
|
|
21
|
+
*/
|
|
22
|
+
export const inheritableSearchAttributes = searchAttributes => {
|
|
23
|
+
const entries = Object.entries( searchAttributes ?? {} ).filter( ( [ k ] ) => !isSystemAttribute( k ) );
|
|
24
|
+
return entries.length ? Object.fromEntries( entries ) : undefined;
|
|
25
|
+
};
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { serializeSearchAttributes } from './search_attributes.js';
|
|
2
|
+
|
|
1
3
|
export const createWorkflowDetails = info => ( {
|
|
2
4
|
attempt: info.attempt,
|
|
3
5
|
continuedFromExecutionRunId: info.continuedFromExecutionRunId,
|
|
@@ -8,5 +10,6 @@ export const createWorkflowDetails = info => ( {
|
|
|
8
10
|
runStartTime: info.runStartTime.getTime(),
|
|
9
11
|
startTime: info.startTime.getTime(),
|
|
10
12
|
workflowId: info.workflowId,
|
|
11
|
-
workflowType: info.workflowType
|
|
13
|
+
workflowType: info.workflowType,
|
|
14
|
+
searchAttributes: serializeSearchAttributes( info.searchAttributes )
|
|
12
15
|
} );
|
package/src/hooks/index.d.ts
CHANGED
|
@@ -77,6 +77,12 @@ export interface WorkflowDetails {
|
|
|
77
77
|
* Starts at 1 and increments for every retry if there is a `retryPolicy`
|
|
78
78
|
*/
|
|
79
79
|
attempt: number;
|
|
80
|
+
/**
|
|
81
|
+
* Temporal search attributes set on this workflow execution, if any. Datetime values are ISO strings.
|
|
82
|
+
* Uses the untyped form because `typedSearchAttributes` does not survive the sink/activity-header
|
|
83
|
+
* serialization boundary (https://github.com/temporalio/sdk-typescript/issues/1635).
|
|
84
|
+
*/
|
|
85
|
+
searchAttributes?: Record<string, string[] | number[] | boolean[]>;
|
|
80
86
|
}
|
|
81
87
|
|
|
82
88
|
/**
|
|
@@ -185,14 +191,6 @@ export type ErrorHookPayload =
|
|
|
185
191
|
*/
|
|
186
192
|
export declare function onError( handler: ( payload: ErrorHookPayload ) => void ): void;
|
|
187
193
|
|
|
188
|
-
/**
|
|
189
|
-
* Register a handler to be invoked once, before the worker starts processing tasks.
|
|
190
|
-
* It is invoked before Worker.create().
|
|
191
|
-
*
|
|
192
|
-
* @param handler - Function called with no arguments.
|
|
193
|
-
*/
|
|
194
|
-
export declare function onBeforeWorkerStart( handler: () => void ): void;
|
|
195
|
-
|
|
196
194
|
/**
|
|
197
195
|
* Register a handler to be invoked when a workflow run starts.
|
|
198
196
|
*
|
package/src/hooks/index.js
CHANGED
|
@@ -33,8 +33,6 @@ const onError = cb => {
|
|
|
33
33
|
callHookCb( cb, { source: 'runtime', ...payload }, 'onError' ) );
|
|
34
34
|
};
|
|
35
35
|
|
|
36
|
-
const onBeforeWorkerStart = cb => mainEventBus.on( BusEventType.WORKER_BEFORE_START, () => callHookCb( cb, undefined, 'onBeforeWorkerStart' ) );
|
|
37
|
-
|
|
38
36
|
// Workflow Life-cycle
|
|
39
37
|
// --------------------------------------
|
|
40
38
|
const onWorkflowStart = cb => mainEventBus.on( BusEventType.WORKFLOW_START, payload => callHookCb( cb, payload, 'onWorkflowStart' ) );
|
|
@@ -68,7 +66,6 @@ export {
|
|
|
68
66
|
onActivityEnd,
|
|
69
67
|
onActivityError,
|
|
70
68
|
onActivityStart,
|
|
71
|
-
onBeforeWorkerStart,
|
|
72
69
|
onError,
|
|
73
70
|
onWorkflowEnd,
|
|
74
71
|
onWorkflowError,
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { proxyActivities, inWorkflowContext, executeChild, workflowInfo, uuid4, ParentClosePolicy, upsertMemo } from '@temporalio/workflow';
|
|
3
3
|
import { WorkflowValidator } from './validations/index.js';
|
|
4
4
|
import { toUrlSafeBase64 } from '#helpers/string';
|
|
5
|
+
import { inheritableSearchAttributes } from '#helpers/search_attributes';
|
|
5
6
|
import { WorkflowContext } from '#helpers/workflow_context';
|
|
6
7
|
import { deepMerge } from '#helpers/object';
|
|
7
8
|
import { defaultOptions } from './workflow_activity_options.js';
|
|
@@ -48,7 +49,7 @@ export function workflow( { name, description, inputSchema, outputSchema, fn, op
|
|
|
48
49
|
);
|
|
49
50
|
}
|
|
50
51
|
|
|
51
|
-
const { workflowId, runId, memo, root } = workflowInfo();
|
|
52
|
+
const { workflowId, runId, memo, root, searchAttributes } = workflowInfo();
|
|
52
53
|
const isRoot = !root;
|
|
53
54
|
|
|
54
55
|
checkGlobalContextContamination( runId );
|
|
@@ -59,7 +60,14 @@ export function workflow( { name, description, inputSchema, outputSchema, fn, op
|
|
|
59
60
|
const parentClosePolicy = ParentClosePolicy[invocationOptions?.detached ? 'ABANDON' : 'TERMINATE'];
|
|
60
61
|
const childWorkflowId = `${workflowId}-${toUrlSafeBase64( uuid4() )}`;
|
|
61
62
|
const args = [ input, { activityOptions: invocationOptions?.activityOptions } ];
|
|
62
|
-
|
|
63
|
+
const childSearchAttributes = inheritableSearchAttributes( searchAttributes );
|
|
64
|
+
return executeChild( name, {
|
|
65
|
+
args,
|
|
66
|
+
workflowId: childWorkflowId,
|
|
67
|
+
parentClosePolicy,
|
|
68
|
+
memo,
|
|
69
|
+
...( childSearchAttributes && { searchAttributes: childSearchAttributes } )
|
|
70
|
+
} );
|
|
63
71
|
}
|
|
64
72
|
|
|
65
73
|
// Resolve the activity options: invocation options > definition options > parent options > default options, then enforce final SDK options
|
package/src/worker/index.js
CHANGED
|
@@ -23,6 +23,7 @@ import { serializeError } from '#helpers/error_serializer';
|
|
|
23
23
|
import { setupTemporalLogger } from './temporal_logger.js';
|
|
24
24
|
import { WorkerRunner } from './worker_runner.js';
|
|
25
25
|
import { shutdownServices } from './shutdown.js';
|
|
26
|
+
import { init as initCredentials } from '#credentials/init';
|
|
26
27
|
|
|
27
28
|
import './log_hooks.js';
|
|
28
29
|
|
|
@@ -76,7 +77,8 @@ const execute = async () => {
|
|
|
76
77
|
log.info( 'Loading activities...', { callerDir } );
|
|
77
78
|
const { activities } = await run( () => loadActivities( callerDir, workflows ) );
|
|
78
79
|
|
|
79
|
-
|
|
80
|
+
log.info( 'Initializing credentials...' );
|
|
81
|
+
run( initCredentials );
|
|
80
82
|
|
|
81
83
|
log.info( 'Initializing tracing...' );
|
|
82
84
|
await run( initTracing );
|