@mesh-tech/mesh-cli 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin/mesh.d.ts +16 -0
- package/dist/bin/mesh.d.ts.map +1 -0
- package/dist/bin/mesh.js +40 -0
- package/dist/bin/mesh.js.map +1 -0
- package/dist/src/commands/create-app.d.ts +10 -0
- package/dist/src/commands/create-app.d.ts.map +1 -0
- package/dist/src/commands/create-app.js +176 -0
- package/dist/src/commands/create-app.js.map +1 -0
- package/dist/src/commands/db/connect.d.ts +13 -0
- package/dist/src/commands/db/connect.d.ts.map +1 -0
- package/dist/src/commands/db/connect.js +68 -0
- package/dist/src/commands/db/connect.js.map +1 -0
- package/dist/src/commands/db/credentials.d.ts +16 -0
- package/dist/src/commands/db/credentials.d.ts.map +1 -0
- package/dist/src/commands/db/credentials.js +56 -0
- package/dist/src/commands/db/credentials.js.map +1 -0
- package/dist/src/commands/db/env.d.ts +15 -0
- package/dist/src/commands/db/env.d.ts.map +1 -0
- package/dist/src/commands/db/env.js +50 -0
- package/dist/src/commands/db/env.js.map +1 -0
- package/dist/src/commands/db/exec.d.ts +54 -0
- package/dist/src/commands/db/exec.d.ts.map +1 -0
- package/dist/src/commands/db/exec.js +331 -0
- package/dist/src/commands/db/exec.js.map +1 -0
- package/dist/src/commands/db/index.d.ts +12 -0
- package/dist/src/commands/db/index.d.ts.map +1 -0
- package/dist/src/commands/db/index.js +62 -0
- package/dist/src/commands/db/index.js.map +1 -0
- package/dist/src/commands/db/psql.d.ts +16 -0
- package/dist/src/commands/db/psql.d.ts.map +1 -0
- package/dist/src/commands/db/psql.js +133 -0
- package/dist/src/commands/db/psql.js.map +1 -0
- package/dist/src/commands/deploy.d.ts +24 -0
- package/dist/src/commands/deploy.d.ts.map +1 -0
- package/dist/src/commands/deploy.js +228 -0
- package/dist/src/commands/deploy.js.map +1 -0
- package/dist/src/commands/dev.d.ts +17 -0
- package/dist/src/commands/dev.d.ts.map +1 -0
- package/dist/src/commands/dev.js +1248 -0
- package/dist/src/commands/dev.js.map +1 -0
- package/dist/src/commands/devbox.d.ts +22 -0
- package/dist/src/commands/devbox.d.ts.map +1 -0
- package/dist/src/commands/devbox.js +463 -0
- package/dist/src/commands/devbox.js.map +1 -0
- package/dist/src/commands/login.d.ts +47 -0
- package/dist/src/commands/login.d.ts.map +1 -0
- package/dist/src/commands/login.js +344 -0
- package/dist/src/commands/login.js.map +1 -0
- package/dist/src/commands/registry.d.ts +20 -0
- package/dist/src/commands/registry.d.ts.map +1 -0
- package/dist/src/commands/registry.js +336 -0
- package/dist/src/commands/registry.js.map +1 -0
- package/dist/src/commands/secrets/exec.d.ts +17 -0
- package/dist/src/commands/secrets/exec.d.ts.map +1 -0
- package/dist/src/commands/secrets/exec.js +132 -0
- package/dist/src/commands/secrets/exec.js.map +1 -0
- package/dist/src/commands/secrets/index.d.ts +10 -0
- package/dist/src/commands/secrets/index.d.ts.map +1 -0
- package/dist/src/commands/secrets/index.js +35 -0
- package/dist/src/commands/secrets/index.js.map +1 -0
- package/dist/src/commands/secrets/set.d.ts +22 -0
- package/dist/src/commands/secrets/set.d.ts.map +1 -0
- package/dist/src/commands/secrets/set.js +234 -0
- package/dist/src/commands/secrets/set.js.map +1 -0
- package/dist/src/commands/stack.d.ts +17 -0
- package/dist/src/commands/stack.d.ts.map +1 -0
- package/dist/src/commands/stack.js +175 -0
- package/dist/src/commands/stack.js.map +1 -0
- package/dist/src/commands/tunnel/index.d.ts +14 -0
- package/dist/src/commands/tunnel/index.d.ts.map +1 -0
- package/dist/src/commands/tunnel/index.js +217 -0
- package/dist/src/commands/tunnel/index.js.map +1 -0
- package/dist/src/index.d.ts +7 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +7 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/utils/bastion.d.ts +43 -0
- package/dist/src/utils/bastion.d.ts.map +1 -0
- package/dist/src/utils/bastion.js +77 -0
- package/dist/src/utils/bastion.js.map +1 -0
- package/dist/src/utils/context.d.ts +29 -0
- package/dist/src/utils/context.d.ts.map +1 -0
- package/dist/src/utils/context.js +132 -0
- package/dist/src/utils/context.js.map +1 -0
- package/dist/src/utils/credentials.d.ts +61 -0
- package/dist/src/utils/credentials.d.ts.map +1 -0
- package/dist/src/utils/credentials.js +145 -0
- package/dist/src/utils/credentials.js.map +1 -0
- package/dist/src/utils/index.d.ts +9 -0
- package/dist/src/utils/index.d.ts.map +1 -0
- package/dist/src/utils/index.js +9 -0
- package/dist/src/utils/index.js.map +1 -0
- package/dist/src/utils/log.d.ts +8 -0
- package/dist/src/utils/log.d.ts.map +1 -0
- package/dist/src/utils/log.js +17 -0
- package/dist/src/utils/log.js.map +1 -0
- package/dist/src/utils/pulumi.d.ts +24 -0
- package/dist/src/utils/pulumi.d.ts.map +1 -0
- package/dist/src/utils/pulumi.js +63 -0
- package/dist/src/utils/pulumi.js.map +1 -0
- package/package.json +50 -0
- package/templates/api-web-db/api/.dockerignore +2 -0
- package/templates/api-web-db/api/Dockerfile +16 -0
- package/templates/api-web-db/api/package.json.hbs +21 -0
- package/templates/api-web-db/api/src/index.ts.hbs +123 -0
- package/templates/api-web-db/api/tsconfig.json +13 -0
- package/templates/api-web-db/package.json.hbs +27 -0
- package/templates/api-web-db/sst.config.ts.hbs +151 -0
- package/templates/api-web-db/tsconfig.json +11 -0
- package/templates/api-web-db/web/.dockerignore +3 -0
- package/templates/api-web-db/web/Dockerfile +17 -0
- package/templates/api-web-db/web/app/lib/db.server.ts +36 -0
- package/templates/api-web-db/web/app/root.tsx +29 -0
- package/templates/api-web-db/web/app/routes/home.tsx.hbs +114 -0
- package/templates/api-web-db/web/app/routes.ts +3 -0
- package/templates/api-web-db/web/package.json.hbs +30 -0
- package/templates/api-web-db/web/prisma/schema.prisma +25 -0
- package/templates/api-web-db/web/prisma.config.ts +18 -0
- package/templates/api-web-db/web/react-router.config.ts +5 -0
- package/templates/api-web-db/web/tsconfig.json +19 -0
- package/templates/api-web-db/web/vite.config.ts +9 -0
- package/templates/temporal-api-worker/README.md.hbs +133 -0
- package/templates/temporal-api-worker/api/.dockerignore +4 -0
- package/templates/temporal-api-worker/api/Dockerfile +13 -0
- package/templates/temporal-api-worker/api/package.json.hbs +23 -0
- package/templates/temporal-api-worker/api/src/encryption-codec.ts +133 -0
- package/templates/temporal-api-worker/api/src/index.ts.hbs +145 -0
- package/templates/temporal-api-worker/api/tsconfig.json +14 -0
- package/templates/temporal-api-worker/package.json.hbs +29 -0
- package/templates/temporal-api-worker/sst.config.ts.hbs +161 -0
- package/templates/temporal-api-worker/tsconfig.json +10 -0
- package/templates/temporal-api-worker/worker/.dockerignore +4 -0
- package/templates/temporal-api-worker/worker/Dockerfile +11 -0
- package/templates/temporal-api-worker/worker/package.json.hbs +25 -0
- package/templates/temporal-api-worker/worker/src/activities.ts.hbs +49 -0
- package/templates/temporal-api-worker/worker/src/bundle-workflows.ts +38 -0
- package/templates/temporal-api-worker/worker/src/encryption-codec.ts +156 -0
- package/templates/temporal-api-worker/worker/src/worker.ts.hbs +93 -0
- package/templates/temporal-api-worker/worker/src/workflows.ts.hbs +60 -0
- package/templates/temporal-api-worker/worker/tsconfig.json +14 -0
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Temporal Payload Encryption Codec
|
|
3
|
+
*
|
|
4
|
+
* Encrypts workflow payloads using AES-256-GCM following Temporal's
|
|
5
|
+
* recommended pattern: serialize the entire payload with Protobuf,
|
|
6
|
+
* then encrypt.
|
|
7
|
+
*
|
|
8
|
+
* Reference: https://github.com/temporalio/samples-typescript/blob/main/encryption/src/encryption-codec.ts
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { webcrypto as crypto } from "node:crypto";
|
|
12
|
+
import {
|
|
13
|
+
METADATA_ENCODING_KEY,
|
|
14
|
+
Payload,
|
|
15
|
+
PayloadCodec,
|
|
16
|
+
ValueError,
|
|
17
|
+
} from "@temporalio/common";
|
|
18
|
+
import proto from "@temporalio/proto";
|
|
19
|
+
|
|
20
|
+
const { temporal } = proto;
|
|
21
|
+
|
|
22
|
+
const ENCODING = "binary/encrypted";
|
|
23
|
+
const METADATA_ENCRYPTION_KEY_ID = "encryption-key-id";
|
|
24
|
+
const IV_LENGTH = 12;
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Derive a CryptoKey from an encoding key string using SHA-256
|
|
28
|
+
*/
|
|
29
|
+
async function deriveKey(encodingKey: string): Promise<crypto.CryptoKey> {
|
|
30
|
+
const keyData = await crypto.subtle.digest(
|
|
31
|
+
"SHA-256",
|
|
32
|
+
new TextEncoder().encode(encodingKey)
|
|
33
|
+
);
|
|
34
|
+
return crypto.subtle.importKey("raw", keyData, { name: "AES-GCM" }, false, [
|
|
35
|
+
"encrypt",
|
|
36
|
+
"decrypt",
|
|
37
|
+
]);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Encrypt data using AES-256-GCM
|
|
42
|
+
* Output: IV (12 bytes) + Ciphertext+AuthTag (WebCrypto format)
|
|
43
|
+
*/
|
|
44
|
+
async function encrypt(
|
|
45
|
+
data: Uint8Array,
|
|
46
|
+
key: crypto.CryptoKey
|
|
47
|
+
): Promise<Uint8Array> {
|
|
48
|
+
const iv = crypto.getRandomValues(new Uint8Array(IV_LENGTH));
|
|
49
|
+
const encrypted = await crypto.subtle.encrypt(
|
|
50
|
+
{ name: "AES-GCM", iv },
|
|
51
|
+
key,
|
|
52
|
+
data
|
|
53
|
+
);
|
|
54
|
+
// Prepend IV to ciphertext+authTag
|
|
55
|
+
const result = new Uint8Array(IV_LENGTH + encrypted.byteLength);
|
|
56
|
+
result.set(iv, 0);
|
|
57
|
+
result.set(new Uint8Array(encrypted), IV_LENGTH);
|
|
58
|
+
return result;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Decrypt data using AES-256-GCM
|
|
63
|
+
* Input: IV (12 bytes) + Ciphertext+AuthTag
|
|
64
|
+
*/
|
|
65
|
+
async function decrypt(
|
|
66
|
+
data: Uint8Array,
|
|
67
|
+
key: crypto.CryptoKey
|
|
68
|
+
): Promise<Uint8Array> {
|
|
69
|
+
const iv = data.slice(0, IV_LENGTH);
|
|
70
|
+
const ciphertextWithTag = data.slice(IV_LENGTH);
|
|
71
|
+
const decrypted = await crypto.subtle.decrypt(
|
|
72
|
+
{ name: "AES-GCM", iv },
|
|
73
|
+
key,
|
|
74
|
+
ciphertextWithTag
|
|
75
|
+
);
|
|
76
|
+
return new Uint8Array(decrypted);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Payload codec that encrypts/decrypts payloads using AES-256-GCM.
|
|
81
|
+
*
|
|
82
|
+
* Uses Protobuf serialization for the entire payload (data + metadata)
|
|
83
|
+
* following Temporal's recommended pattern.
|
|
84
|
+
*/
|
|
85
|
+
export class EncryptionCodec implements PayloadCodec {
|
|
86
|
+
private constructor(
|
|
87
|
+
private readonly key: crypto.CryptoKey,
|
|
88
|
+
private readonly keyId: string
|
|
89
|
+
) {}
|
|
90
|
+
|
|
91
|
+
static async create(encodingKey: string): Promise<EncryptionCodec> {
|
|
92
|
+
const key = await deriveKey(encodingKey);
|
|
93
|
+
// Use truncated hash as key ID for identification
|
|
94
|
+
const keyIdData = await crypto.subtle.digest(
|
|
95
|
+
"SHA-256",
|
|
96
|
+
new TextEncoder().encode(encodingKey)
|
|
97
|
+
);
|
|
98
|
+
const keyId = Buffer.from(keyIdData).toString("hex").slice(0, 16);
|
|
99
|
+
return new EncryptionCodec(key, keyId);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
async encode(payloads: Payload[]): Promise<Payload[]> {
|
|
103
|
+
return Promise.all(
|
|
104
|
+
payloads.map(async (payload) => ({
|
|
105
|
+
metadata: {
|
|
106
|
+
[METADATA_ENCODING_KEY]: new TextEncoder().encode(ENCODING),
|
|
107
|
+
[METADATA_ENCRYPTION_KEY_ID]: new TextEncoder().encode(this.keyId),
|
|
108
|
+
},
|
|
109
|
+
// Serialize entire payload with Protobuf, then encrypt
|
|
110
|
+
data: await encrypt(
|
|
111
|
+
temporal.api.common.v1.Payload.encode(payload).finish(),
|
|
112
|
+
this.key
|
|
113
|
+
),
|
|
114
|
+
}))
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
async decode(payloads: Payload[]): Promise<Payload[]> {
|
|
119
|
+
return Promise.all(
|
|
120
|
+
payloads.map(async (payload) => {
|
|
121
|
+
// Check if encrypted
|
|
122
|
+
const encoding = payload.metadata?.[METADATA_ENCODING_KEY];
|
|
123
|
+
if (!encoding) {
|
|
124
|
+
return payload;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const encodingStr = new TextDecoder().decode(encoding);
|
|
128
|
+
if (encodingStr !== ENCODING) {
|
|
129
|
+
return payload;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (!payload.data) {
|
|
133
|
+
throw new ValueError("Payload data is missing");
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Decrypt and deserialize with Protobuf
|
|
137
|
+
const decryptedBytes = await decrypt(payload.data, this.key);
|
|
138
|
+
return temporal.api.common.v1.Payload.decode(decryptedBytes);
|
|
139
|
+
})
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Creates an encryption codec from TEMPORAL_ENCODING_KEY env var.
|
|
146
|
+
* Returns undefined if not set.
|
|
147
|
+
*/
|
|
148
|
+
export async function createEncryptionCodec(): Promise<EncryptionCodec | undefined> {
|
|
149
|
+
const encodingKey = process.env.TEMPORAL_ENCODING_KEY;
|
|
150
|
+
if (!encodingKey) {
|
|
151
|
+
console.log("[Encryption] No TEMPORAL_ENCODING_KEY set, payloads will not be encrypted");
|
|
152
|
+
return undefined;
|
|
153
|
+
}
|
|
154
|
+
console.log("[Encryption] Payload encryption enabled");
|
|
155
|
+
return EncryptionCodec.create(encodingKey);
|
|
156
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* {{titleCase name}} Temporal Worker
|
|
3
|
+
*
|
|
4
|
+
* Environment variables:
|
|
5
|
+
* - TEMPORAL_ADDRESS: Temporal server address (default: localhost:7233)
|
|
6
|
+
* - TEMPORAL_NAMESPACE: Temporal namespace (required)
|
|
7
|
+
* - TEMPORAL_TASK_QUEUE: Task queue name (required)
|
|
8
|
+
* - TEMPORAL_ENCODING_KEY: Encoding key for payload encryption (optional)
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import * as fs from "fs";
|
|
12
|
+
import * as path from "path";
|
|
13
|
+
import { fileURLToPath } from "url";
|
|
14
|
+
import { NativeConnection, Worker, WorkerOptions, bundleWorkflowCode } from "@temporalio/worker";
|
|
15
|
+
import * as activities from "./activities.js";
|
|
16
|
+
import { createEncryptionCodec } from "./encryption-codec.js";
|
|
17
|
+
|
|
18
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
19
|
+
|
|
20
|
+
const TEMPORAL_ADDRESS = process.env.TEMPORAL_ADDRESS ?? "localhost:7233";
|
|
21
|
+
|
|
22
|
+
function requireEnv(name: string): string {
|
|
23
|
+
const value = process.env[name];
|
|
24
|
+
if (!value) {
|
|
25
|
+
throw new Error(`${name} environment variable is required`);
|
|
26
|
+
}
|
|
27
|
+
return value;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const TEMPORAL_NAMESPACE = requireEnv("TEMPORAL_NAMESPACE");
|
|
31
|
+
const TEMPORAL_TASK_QUEUE = requireEnv("TEMPORAL_TASK_QUEUE");
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Load workflows - uses pre-bundled code in production, source files in dev
|
|
35
|
+
*/
|
|
36
|
+
async function getWorkflowOptions(): Promise<
|
|
37
|
+
Pick<WorkerOptions, "workflowBundle" | "workflowsPath">
|
|
38
|
+
> {
|
|
39
|
+
const bundlePath = path.join(__dirname, "workflows-bundle.js");
|
|
40
|
+
|
|
41
|
+
if (fs.existsSync(bundlePath)) {
|
|
42
|
+
console.log("[Worker] Loading pre-bundled workflows...");
|
|
43
|
+
return {
|
|
44
|
+
workflowBundle: { code: fs.readFileSync(bundlePath, "utf-8") },
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
console.log("[Worker] Bundling workflows from source...");
|
|
49
|
+
return {
|
|
50
|
+
workflowBundle: await bundleWorkflowCode({
|
|
51
|
+
workflowsPath: new URL("./workflows.js", import.meta.url).pathname,
|
|
52
|
+
}),
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
async function run() {
|
|
57
|
+
console.log(`[Worker] Connecting to Temporal at ${TEMPORAL_ADDRESS}...`);
|
|
58
|
+
|
|
59
|
+
const connection = await NativeConnection.connect({
|
|
60
|
+
address: TEMPORAL_ADDRESS,
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
console.log(`[Worker] Connected. Namespace: ${TEMPORAL_NAMESPACE}`);
|
|
64
|
+
|
|
65
|
+
const workflowOptions = await getWorkflowOptions();
|
|
66
|
+
|
|
67
|
+
// Set up payload encryption if encoding key is available
|
|
68
|
+
const encryptionCodec = await createEncryptionCodec();
|
|
69
|
+
const dataConverter = encryptionCodec
|
|
70
|
+
? { payloadCodecs: [encryptionCodec] }
|
|
71
|
+
: undefined;
|
|
72
|
+
|
|
73
|
+
if (encryptionCodec) {
|
|
74
|
+
console.log("[Worker] Payload encryption enabled");
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const worker = await Worker.create({
|
|
78
|
+
connection,
|
|
79
|
+
namespace: TEMPORAL_NAMESPACE,
|
|
80
|
+
taskQueue: TEMPORAL_TASK_QUEUE,
|
|
81
|
+
...workflowOptions,
|
|
82
|
+
activities,
|
|
83
|
+
dataConverter,
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
console.log(`[Worker] Starting on task queue: ${TEMPORAL_TASK_QUEUE}`);
|
|
87
|
+
await worker.run();
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
run().catch((err) => {
|
|
91
|
+
console.error("[Worker] Fatal error:", err);
|
|
92
|
+
process.exit(1);
|
|
93
|
+
});
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* {{titleCase name}} Workflows
|
|
3
|
+
*
|
|
4
|
+
* Define your workflow logic here. Workflows are deterministic functions
|
|
5
|
+
* that orchestrate activities and other workflows.
|
|
6
|
+
*
|
|
7
|
+
* Rules for workflows:
|
|
8
|
+
* - Must be deterministic (same inputs = same outputs)
|
|
9
|
+
* - Cannot use Date.now(), Math.random(), or other non-deterministic APIs
|
|
10
|
+
* - Use sleep() instead of setTimeout()
|
|
11
|
+
* - Use proxyActivities() to call activities
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { proxyActivities, sleep } from "@temporalio/workflow";
|
|
15
|
+
import type * as activities from "./activities.js";
|
|
16
|
+
|
|
17
|
+
// Configure activity retries and timeouts
|
|
18
|
+
const { greet, processTask } = proxyActivities<typeof activities>({
|
|
19
|
+
startToCloseTimeout: "30 seconds",
|
|
20
|
+
retry: {
|
|
21
|
+
maximumAttempts: 3,
|
|
22
|
+
},
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Simple greeting workflow
|
|
27
|
+
*
|
|
28
|
+
* @param name - Name to greet
|
|
29
|
+
* @returns Greeting message
|
|
30
|
+
*/
|
|
31
|
+
export async function greetingWorkflow(name: string): Promise<string> {
|
|
32
|
+
return await greet(name);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Task processing workflow with notification
|
|
37
|
+
*
|
|
38
|
+
* @param taskId - Task identifier
|
|
39
|
+
* @param userId - User who owns the task
|
|
40
|
+
* @returns Task status
|
|
41
|
+
*/
|
|
42
|
+
export async function taskWorkflow(
|
|
43
|
+
taskId: string,
|
|
44
|
+
userId: string
|
|
45
|
+
): Promise<{ status: string; taskId: string }> {
|
|
46
|
+
console.log(`[Workflow] Processing task ${taskId} for user ${userId}`);
|
|
47
|
+
|
|
48
|
+
// Process the task
|
|
49
|
+
const result = await processTask(taskId);
|
|
50
|
+
|
|
51
|
+
// Wait before completing (demonstrates durable timers)
|
|
52
|
+
await sleep("2 seconds");
|
|
53
|
+
|
|
54
|
+
console.log(`[Workflow] Task ${taskId} completed with status: ${result}`);
|
|
55
|
+
|
|
56
|
+
return {
|
|
57
|
+
status: result,
|
|
58
|
+
taskId,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "NodeNext",
|
|
5
|
+
"moduleResolution": "NodeNext",
|
|
6
|
+
"outDir": "./dist",
|
|
7
|
+
"rootDir": "./src",
|
|
8
|
+
"strict": true,
|
|
9
|
+
"esModuleInterop": true,
|
|
10
|
+
"skipLibCheck": true,
|
|
11
|
+
"declaration": true
|
|
12
|
+
},
|
|
13
|
+
"include": ["src/**/*"]
|
|
14
|
+
}
|