@terraforge/core 0.0.18 → 0.0.20
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/index.d.mts +77 -14
- package/dist/index.mjs +193 -50
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { S3Client } from "@aws-sdk/client-s3";
|
|
2
1
|
import { DynamoDB } from "@aws-sdk/client-dynamodb";
|
|
2
|
+
import { S3Client } from "@aws-sdk/client-s3";
|
|
3
3
|
import { UUID } from "node:crypto";
|
|
4
4
|
import { AwsCredentialIdentity, AwsCredentialIdentityProvider } from "@aws-sdk/types";
|
|
5
5
|
|
|
@@ -162,6 +162,11 @@ type ResourceStatusInfo = {
|
|
|
162
162
|
tag: 'resource' | 'data';
|
|
163
163
|
status: ResourceStatus;
|
|
164
164
|
};
|
|
165
|
+
type StackStatusInfo = {
|
|
166
|
+
name: string;
|
|
167
|
+
urn: URN;
|
|
168
|
+
resources: ResourceStatusInfo[];
|
|
169
|
+
};
|
|
165
170
|
//#endregion
|
|
166
171
|
//#region src/backend/lock.d.ts
|
|
167
172
|
type LockBackend = {
|
|
@@ -176,6 +181,7 @@ type AppState = {
|
|
|
176
181
|
version?: number;
|
|
177
182
|
idempotentToken?: UUID;
|
|
178
183
|
stacks: Record<URN, StackState>;
|
|
184
|
+
pendingDeletes?: Record<URN, NodeState>;
|
|
179
185
|
};
|
|
180
186
|
type StackState = {
|
|
181
187
|
name: string;
|
|
@@ -202,6 +208,20 @@ type StateBackend = {
|
|
|
202
208
|
delete(urn: URN): Promise<void>;
|
|
203
209
|
};
|
|
204
210
|
//#endregion
|
|
211
|
+
//#region src/backend/activity-log.d.ts
|
|
212
|
+
type LogProps = {
|
|
213
|
+
action: 'deploy' | 'delete';
|
|
214
|
+
filters?: string[];
|
|
215
|
+
};
|
|
216
|
+
type Log = LogProps & {
|
|
217
|
+
user?: string;
|
|
218
|
+
date?: number;
|
|
219
|
+
};
|
|
220
|
+
type ActivityLogBackend = {
|
|
221
|
+
log(urn: URN, log: LogProps): Promise<void>;
|
|
222
|
+
tail(urn: URN): Promise<Log[]>;
|
|
223
|
+
};
|
|
224
|
+
//#endregion
|
|
205
225
|
//#region src/provider.d.ts
|
|
206
226
|
type CreateProps<T = State> = {
|
|
207
227
|
type: string;
|
|
@@ -313,6 +333,7 @@ type WorkSpaceOptions = {
|
|
|
313
333
|
backend: {
|
|
314
334
|
state: StateBackend;
|
|
315
335
|
lock: LockBackend;
|
|
336
|
+
activityLog?: ActivityLogBackend;
|
|
316
337
|
};
|
|
317
338
|
hooks?: Hooks;
|
|
318
339
|
};
|
|
@@ -338,7 +359,7 @@ declare class WorkSpace {
|
|
|
338
359
|
/**
|
|
339
360
|
* Get the status of all resources in the app by comparing current config with state file.
|
|
340
361
|
*/
|
|
341
|
-
status(app: App): Promise<
|
|
362
|
+
status(app: App): Promise<StackStatusInfo[]>;
|
|
342
363
|
protected destroyProviders(): Promise<void>;
|
|
343
364
|
}
|
|
344
365
|
//#endregion
|
|
@@ -361,6 +382,19 @@ declare class AppError extends Error {
|
|
|
361
382
|
declare class ResourceNotFound extends Error {}
|
|
362
383
|
declare class ResourceAlreadyExists extends Error {}
|
|
363
384
|
//#endregion
|
|
385
|
+
//#region src/backend/memory/activity-log.d.ts
|
|
386
|
+
type Props$4 = {
|
|
387
|
+
user?: string;
|
|
388
|
+
};
|
|
389
|
+
declare class MemoryActivityLogBackend implements ActivityLogBackend {
|
|
390
|
+
private props;
|
|
391
|
+
protected groups: Map<`urn:${string}`, Log[]>;
|
|
392
|
+
constructor(props?: Props$4);
|
|
393
|
+
log(urn: URN, log: LogProps): Promise<void>;
|
|
394
|
+
private getLogGroup;
|
|
395
|
+
tail(urn: URN, limit?: number): Promise<Log[]>;
|
|
396
|
+
}
|
|
397
|
+
//#endregion
|
|
364
398
|
//#region src/backend/memory/state.d.ts
|
|
365
399
|
declare class MemoryStateBackend implements StateBackend {
|
|
366
400
|
protected states: Map<`urn:${string}`, AppState>;
|
|
@@ -379,6 +413,20 @@ declare class MemoryLockBackend implements LockBackend {
|
|
|
379
413
|
clear(): void;
|
|
380
414
|
}
|
|
381
415
|
//#endregion
|
|
416
|
+
//#region src/backend/file/activity-log.d.ts
|
|
417
|
+
type Props$3 = {
|
|
418
|
+
user?: string;
|
|
419
|
+
dir: string;
|
|
420
|
+
};
|
|
421
|
+
declare class FileActivityLogBackend implements ActivityLogBackend {
|
|
422
|
+
private props;
|
|
423
|
+
constructor(props: Props$3);
|
|
424
|
+
private logFile;
|
|
425
|
+
private mkdir;
|
|
426
|
+
log(urn: URN, log: LogProps): Promise<void>;
|
|
427
|
+
tail(urn: URN, limit?: number): Promise<Log[]>;
|
|
428
|
+
}
|
|
429
|
+
//#endregion
|
|
382
430
|
//#region src/backend/file/state.d.ts
|
|
383
431
|
declare class FileStateBackend implements StateBackend {
|
|
384
432
|
private props;
|
|
@@ -405,23 +453,23 @@ declare class FileLockBackend implements LockBackend {
|
|
|
405
453
|
lock(urn: URN): Promise<() => Promise<void>>;
|
|
406
454
|
}
|
|
407
455
|
//#endregion
|
|
408
|
-
//#region src/backend/aws/
|
|
409
|
-
type Props$
|
|
456
|
+
//#region src/backend/aws/dynamodb-activity-log.d.ts
|
|
457
|
+
type Props$2 = {
|
|
410
458
|
credentials: AwsCredentialIdentity | AwsCredentialIdentityProvider;
|
|
411
459
|
region: string;
|
|
412
|
-
|
|
460
|
+
tableName: string;
|
|
461
|
+
user?: string;
|
|
413
462
|
};
|
|
414
|
-
declare class
|
|
463
|
+
declare class DynamoDBActivityLogBackend implements ActivityLogBackend {
|
|
415
464
|
private props;
|
|
416
|
-
protected client:
|
|
417
|
-
constructor(props: Props$
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
delete(urn: URN): Promise<void>;
|
|
465
|
+
protected client: DynamoDB;
|
|
466
|
+
constructor(props: Props$2);
|
|
467
|
+
log(urn: URN, log: LogProps): Promise<void>;
|
|
468
|
+
tail(urn: URN, limit?: number): Promise<Log[]>;
|
|
421
469
|
}
|
|
422
470
|
//#endregion
|
|
423
471
|
//#region src/backend/aws/dynamodb-lock.d.ts
|
|
424
|
-
type Props = {
|
|
472
|
+
type Props$1 = {
|
|
425
473
|
credentials: AwsCredentialIdentity | AwsCredentialIdentityProvider;
|
|
426
474
|
region: string;
|
|
427
475
|
tableName: string;
|
|
@@ -429,12 +477,27 @@ type Props = {
|
|
|
429
477
|
declare class DynamoLockBackend implements LockBackend {
|
|
430
478
|
private props;
|
|
431
479
|
protected client: DynamoDB;
|
|
432
|
-
constructor(props: Props);
|
|
480
|
+
constructor(props: Props$1);
|
|
433
481
|
insecureReleaseLock(urn: URN): Promise<void>;
|
|
434
482
|
locked(urn: URN): Promise<boolean>;
|
|
435
483
|
lock(urn: URN): Promise<() => Promise<void>>;
|
|
436
484
|
}
|
|
437
485
|
//#endregion
|
|
486
|
+
//#region src/backend/aws/s3-state.d.ts
|
|
487
|
+
type Props = {
|
|
488
|
+
credentials: AwsCredentialIdentity | AwsCredentialIdentityProvider;
|
|
489
|
+
region: string;
|
|
490
|
+
bucket: string;
|
|
491
|
+
};
|
|
492
|
+
declare class S3StateBackend implements StateBackend {
|
|
493
|
+
private props;
|
|
494
|
+
protected client: S3Client;
|
|
495
|
+
constructor(props: Props);
|
|
496
|
+
get(urn: URN): Promise<any>;
|
|
497
|
+
update(urn: URN, state: AppState): Promise<void>;
|
|
498
|
+
delete(urn: URN): Promise<void>;
|
|
499
|
+
}
|
|
500
|
+
//#endregion
|
|
438
501
|
//#region src/helpers.d.ts
|
|
439
502
|
declare const file: (path: string, encoding?: BufferEncoding) => Future<string>;
|
|
440
503
|
declare const hash: (path: string, algo?: string) => Future<string>;
|
|
@@ -465,4 +528,4 @@ type CustomResourceProvider = Partial<{
|
|
|
465
528
|
}>;
|
|
466
529
|
declare const createCustomProvider: (providerId: string, resourceProviders: Record<string, CustomResourceProvider>) => Provider;
|
|
467
530
|
//#endregion
|
|
468
|
-
export { App, AppError, type Config, type CreateProps, type CustomResourceProvider, type DataSource, type DataSourceFunction, type DataSourceMeta, type DeleteProps, DynamoLockBackend, FileLockBackend, FileStateBackend, Future, type GetDataProps, type GetProps, Group, type Input, LockBackend, MemoryLockBackend, MemoryStateBackend, type Meta, type Node, type OptionalInput, type OptionalOutput, Output, type PlanProps, type ProcedureOptions, type Provider, type Resource, ResourceAlreadyExists, type ResourceClass, type ResourceConfig, ResourceError, type ResourceMeta, ResourceNotFound, type ResourceStatus, type ResourceStatusInfo, S3StateBackend, Stack, type State, StateBackend, type Tag, type URN, type UpdateProps, WorkSpace, type WorkSpaceOptions, createCustomProvider, createCustomResourceClass, createDebugger, createMeta, deferredOutput, enableDebug, findInputDeps, getMeta, isDataSource, isNode, isResource, nodeMetaSymbol, output, resolveInputs };
|
|
531
|
+
export { ActivityLogBackend, App, AppError, type Config, type CreateProps, type CustomResourceProvider, type DataSource, type DataSourceFunction, type DataSourceMeta, type DeleteProps, DynamoDBActivityLogBackend, DynamoLockBackend, FileActivityLogBackend, FileLockBackend, FileStateBackend, Future, type GetDataProps, type GetProps, Group, type Input, LockBackend, Log, LogProps, MemoryActivityLogBackend, MemoryLockBackend, MemoryStateBackend, type Meta, type Node, type OptionalInput, type OptionalOutput, Output, type PlanProps, type ProcedureOptions, type Provider, type Resource, ResourceAlreadyExists, type ResourceClass, type ResourceConfig, ResourceError, type ResourceMeta, ResourceNotFound, type ResourceStatus, type ResourceStatusInfo, S3StateBackend, Stack, type StackStatusInfo, type State, StateBackend, type Tag, type URN, type UpdateProps, WorkSpace, type WorkSpaceOptions, createCustomProvider, createCustomResourceClass, createDebugger, createMeta, deferredOutput, enableDebug, findInputDeps, getMeta, isDataSource, isNode, isResource, nodeMetaSymbol, output, resolveInputs };
|
package/dist/index.mjs
CHANGED
|
@@ -4,12 +4,12 @@ import { DirectedGraph } from "graphology";
|
|
|
4
4
|
import { topologicalGenerations, willCreateCycle } from "graphology-dag";
|
|
5
5
|
import { v5 } from "uuid";
|
|
6
6
|
import { get } from "get-wild";
|
|
7
|
-
import { mkdir, readFile, rm, stat, writeFile } from "node:fs/promises";
|
|
7
|
+
import { appendFile, mkdir, readFile, rm, stat, writeFile } from "node:fs/promises";
|
|
8
8
|
import { join } from "node:path";
|
|
9
9
|
import { lock } from "proper-lockfile";
|
|
10
|
-
import { DeleteObjectCommand, GetObjectCommand, PutObjectCommand, S3Client, S3ServiceException } from "@aws-sdk/client-s3";
|
|
11
10
|
import { DynamoDB } from "@aws-sdk/client-dynamodb";
|
|
12
11
|
import { marshall, unmarshall } from "@aws-sdk/util-dynamodb";
|
|
12
|
+
import { DeleteObjectCommand, GetObjectCommand, PutObjectCommand, S3Client, S3ServiceException } from "@aws-sdk/client-s3";
|
|
13
13
|
import { createHash } from "node:crypto";
|
|
14
14
|
|
|
15
15
|
//#region src/node.ts
|
|
@@ -605,6 +605,10 @@ const deleteResource = async (appToken, urn, state, opt) => {
|
|
|
605
605
|
//#endregion
|
|
606
606
|
//#region src/workspace/procedure/delete-app.ts
|
|
607
607
|
const deleteApp = async (app, opt) => {
|
|
608
|
+
await opt.backend.activityLog?.log(app.urn, {
|
|
609
|
+
action: "delete",
|
|
610
|
+
filters: opt.filters
|
|
611
|
+
});
|
|
608
612
|
const latestState = await opt.backend.state.get(app.urn);
|
|
609
613
|
if (!latestState) throw new AppError(app.name, [], `App already deleted: ${app.name}`);
|
|
610
614
|
const appState = migrateAppState(latestState);
|
|
@@ -623,6 +627,16 @@ const deleteApp = async (app, opt) => {
|
|
|
623
627
|
delete stackState.nodes[urn];
|
|
624
628
|
});
|
|
625
629
|
const errors = await graph.run();
|
|
630
|
+
if (errors.length === 0 && appState.pendingDeletes) {
|
|
631
|
+
for (const [urn, nodeState] of entries(appState.pendingDeletes)) try {
|
|
632
|
+
await deleteResource(appState.idempotentToken, urn, nodeState, opt);
|
|
633
|
+
delete appState.pendingDeletes[urn];
|
|
634
|
+
} catch (error) {
|
|
635
|
+
if (error instanceof Error) errors.push(error);
|
|
636
|
+
else errors.push(/* @__PURE__ */ new Error(`${error}`));
|
|
637
|
+
}
|
|
638
|
+
if (Object.keys(appState.pendingDeletes).length === 0) delete appState.pendingDeletes;
|
|
639
|
+
}
|
|
626
640
|
removeEmptyStackStates(appState);
|
|
627
641
|
delete appState.idempotentToken;
|
|
628
642
|
await opt.backend.state.update(app.urn, appState);
|
|
@@ -813,7 +827,8 @@ const updateResource = async (resource, appToken, priorInputState, priorOutputSt
|
|
|
813
827
|
const idempotantToken = createIdempotantToken(appToken, meta.urn, "update");
|
|
814
828
|
let result;
|
|
815
829
|
debug$2(meta.type);
|
|
816
|
-
debug$2(
|
|
830
|
+
debug$2("prior state", priorOutputState);
|
|
831
|
+
debug$2("proposed state", proposedState);
|
|
817
832
|
try {
|
|
818
833
|
await opt.hooks?.beforeResourceUpdate?.({
|
|
819
834
|
urn: resource.urn,
|
|
@@ -852,6 +867,10 @@ const updateResource = async (resource, appToken, priorInputState, priorOutputSt
|
|
|
852
867
|
const debug$1 = createDebugger("Deploy App");
|
|
853
868
|
const deployApp = async (app, opt) => {
|
|
854
869
|
debug$1(app.name, "start");
|
|
870
|
+
await opt.backend.activityLog?.log(app.urn, {
|
|
871
|
+
action: "deploy",
|
|
872
|
+
filters: opt.filters
|
|
873
|
+
});
|
|
855
874
|
const appState = migrateAppState(await opt.backend.state.get(app.urn) ?? {
|
|
856
875
|
name: app.name,
|
|
857
876
|
stacks: {}
|
|
@@ -883,7 +902,6 @@ const deployApp = async (app, opt) => {
|
|
|
883
902
|
}
|
|
884
903
|
const queue = createConcurrencyQueue(opt.concurrency ?? 10);
|
|
885
904
|
const graph = new DependencyGraph();
|
|
886
|
-
const replacementDeletes = /* @__PURE__ */ new Map();
|
|
887
905
|
const allNodes = {};
|
|
888
906
|
for (const stackState of Object.values(appState.stacks)) for (const [urn, nodeState] of entries(stackState.nodes)) allNodes[urn] = nodeState;
|
|
889
907
|
for (const stack of filteredOutStacks) {
|
|
@@ -966,10 +984,33 @@ const deployApp = async (app, opt) => {
|
|
|
966
984
|
let newResourceState;
|
|
967
985
|
const ignoreReplace = forcedUpdateDependents.has(meta$1.urn);
|
|
968
986
|
if (!ignoreReplace && requiresReplacement(nodeState.input, input, meta$1.config?.replaceOnChanges ?? [])) if (meta$1.config?.createBeforeReplace) {
|
|
987
|
+
for (const [dependentUrn, dependentNode] of nodeByUrn.entries()) {
|
|
988
|
+
if (!isResource(dependentNode)) continue;
|
|
989
|
+
const dependentMeta = getMeta(dependentNode);
|
|
990
|
+
if (!dependentMeta.dependencies.has(meta$1.urn)) continue;
|
|
991
|
+
const dependentStackState = stackStates.get(dependentMeta.stack.urn);
|
|
992
|
+
const dependentState = dependentStackState?.nodes[dependentUrn];
|
|
993
|
+
if (!dependentStackState || !dependentState) continue;
|
|
994
|
+
const dependencyPaths = findDependencyPaths(dependentMeta.input, meta$1.urn);
|
|
995
|
+
if (dependencyPaths.length === 0) continue;
|
|
996
|
+
const dependentProvider = findProvider(opt.providers, dependentMeta.provider);
|
|
997
|
+
if (dependentProvider.planResourceChange) {
|
|
998
|
+
if ((await dependentProvider.planResourceChange({
|
|
999
|
+
type: dependentMeta.type,
|
|
1000
|
+
priorState: dependentState.output,
|
|
1001
|
+
proposedState: input
|
|
1002
|
+
})).requiresReplacement) {
|
|
1003
|
+
if (!allowsDependentReplace(dependentMeta.config?.replaceOnChanges, dependencyPaths)) throw ResourceError.wrap(dependentMeta.urn, dependentMeta.type, "update", /* @__PURE__ */ new Error(`Replacing ${meta$1.urn} requires ${dependentMeta.urn} to set replaceOnChanges for its dependency fields.`));
|
|
1004
|
+
}
|
|
1005
|
+
}
|
|
1006
|
+
}
|
|
969
1007
|
const priorState = { ...nodeState };
|
|
970
1008
|
newResourceState = await createResource(node, appState.idempotentToken, input, opt);
|
|
971
1009
|
if (newResourceState.output) meta$1.resolve(newResourceState.output);
|
|
972
|
-
if (!meta$1.config?.retainOnDelete)
|
|
1010
|
+
if (!meta$1.config?.retainOnDelete) {
|
|
1011
|
+
appState.pendingDeletes ??= {};
|
|
1012
|
+
appState.pendingDeletes[meta$1.urn] = priorState;
|
|
1013
|
+
}
|
|
973
1014
|
} else {
|
|
974
1015
|
for (const [dependentUrn, dependentNode] of nodeByUrn.entries()) {
|
|
975
1016
|
if (!isResource(dependentNode)) continue;
|
|
@@ -1028,11 +1069,15 @@ const deployApp = async (app, opt) => {
|
|
|
1028
1069
|
}
|
|
1029
1070
|
}
|
|
1030
1071
|
const errors = await graph.run();
|
|
1031
|
-
if (errors.length === 0 &&
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1072
|
+
if (errors.length === 0 && appState.pendingDeletes) {
|
|
1073
|
+
for (const [urn, nodeState] of entries(appState.pendingDeletes)) try {
|
|
1074
|
+
await deleteResource(appState.idempotentToken, urn, nodeState, opt);
|
|
1075
|
+
delete appState.pendingDeletes[urn];
|
|
1076
|
+
} catch (error) {
|
|
1077
|
+
if (error instanceof Error) errors.push(error);
|
|
1078
|
+
else errors.push(/* @__PURE__ */ new Error(`${error}`));
|
|
1079
|
+
}
|
|
1080
|
+
if (Object.keys(appState.pendingDeletes).length === 0) delete appState.pendingDeletes;
|
|
1036
1081
|
}
|
|
1037
1082
|
removeEmptyStackStates(appState);
|
|
1038
1083
|
delete appState.idempotentToken;
|
|
@@ -1135,11 +1180,12 @@ const filterStateToMatchConfig = (state, config) => {
|
|
|
1135
1180
|
};
|
|
1136
1181
|
const status = async (app, opt) => {
|
|
1137
1182
|
const appState = await opt.backend.state.get(app.urn);
|
|
1138
|
-
const
|
|
1183
|
+
const stacks = [];
|
|
1139
1184
|
const configuredUrns = /* @__PURE__ */ new Set();
|
|
1140
1185
|
for (const stack of app.stacks) for (const node of stack.nodes) configuredUrns.add(getMeta(node).urn);
|
|
1141
1186
|
for (const stack of app.stacks) {
|
|
1142
1187
|
const stackState = appState?.stacks[stack.urn];
|
|
1188
|
+
const resources = [];
|
|
1143
1189
|
for (const node of stack.nodes) {
|
|
1144
1190
|
const meta = getMeta(node);
|
|
1145
1191
|
const nodeState = stackState?.nodes[meta.urn];
|
|
@@ -1172,18 +1218,31 @@ const status = async (app, opt) => {
|
|
|
1172
1218
|
status: "stale"
|
|
1173
1219
|
});
|
|
1174
1220
|
}
|
|
1221
|
+
stacks.push({
|
|
1222
|
+
name: stack.name,
|
|
1223
|
+
urn: stack.urn,
|
|
1224
|
+
resources
|
|
1225
|
+
});
|
|
1175
1226
|
}
|
|
1176
1227
|
if (appState) {
|
|
1177
1228
|
const configuredStackUrns = new Set(app.stacks.map((s) => s.urn));
|
|
1178
|
-
for (const [stackUrn, stackState] of Object.entries(appState.stacks)) if (!configuredStackUrns.has(stackUrn))
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1229
|
+
for (const [stackUrn, stackState] of Object.entries(appState.stacks)) if (!configuredStackUrns.has(stackUrn)) {
|
|
1230
|
+
const resources = [];
|
|
1231
|
+
for (const [urn, nodeState] of Object.entries(stackState.nodes)) resources.push({
|
|
1232
|
+
urn,
|
|
1233
|
+
type: nodeState.type,
|
|
1234
|
+
provider: nodeState.provider,
|
|
1235
|
+
tag: nodeState.tag,
|
|
1236
|
+
status: "stale"
|
|
1237
|
+
});
|
|
1238
|
+
stacks.push({
|
|
1239
|
+
name: stackState.name,
|
|
1240
|
+
urn: stackUrn,
|
|
1241
|
+
resources
|
|
1242
|
+
});
|
|
1243
|
+
}
|
|
1185
1244
|
}
|
|
1186
|
-
return
|
|
1245
|
+
return stacks;
|
|
1187
1246
|
};
|
|
1188
1247
|
|
|
1189
1248
|
//#endregion
|
|
@@ -1253,6 +1312,29 @@ var WorkSpace = class {
|
|
|
1253
1312
|
}
|
|
1254
1313
|
};
|
|
1255
1314
|
|
|
1315
|
+
//#endregion
|
|
1316
|
+
//#region src/backend/memory/activity-log.ts
|
|
1317
|
+
var MemoryActivityLogBackend = class {
|
|
1318
|
+
groups = /* @__PURE__ */ new Map();
|
|
1319
|
+
constructor(props = {}) {
|
|
1320
|
+
this.props = props;
|
|
1321
|
+
}
|
|
1322
|
+
async log(urn, log) {
|
|
1323
|
+
this.getLogGroup(urn).push({
|
|
1324
|
+
user: this.props.user,
|
|
1325
|
+
date: Date.now(),
|
|
1326
|
+
...log
|
|
1327
|
+
});
|
|
1328
|
+
}
|
|
1329
|
+
getLogGroup(urn) {
|
|
1330
|
+
if (!this.groups.has(urn)) this.groups.set(urn, []);
|
|
1331
|
+
return this.groups.get(urn);
|
|
1332
|
+
}
|
|
1333
|
+
async tail(urn, limit = 10) {
|
|
1334
|
+
return this.getLogGroup(urn).slice(-limit);
|
|
1335
|
+
}
|
|
1336
|
+
};
|
|
1337
|
+
|
|
1256
1338
|
//#endregion
|
|
1257
1339
|
//#region src/backend/memory/state.ts
|
|
1258
1340
|
var MemoryStateBackend = class {
|
|
@@ -1294,6 +1376,34 @@ var MemoryLockBackend = class {
|
|
|
1294
1376
|
}
|
|
1295
1377
|
};
|
|
1296
1378
|
|
|
1379
|
+
//#endregion
|
|
1380
|
+
//#region src/backend/file/activity-log.ts
|
|
1381
|
+
var FileActivityLogBackend = class {
|
|
1382
|
+
constructor(props) {
|
|
1383
|
+
this.props = props;
|
|
1384
|
+
}
|
|
1385
|
+
logFile(urn) {
|
|
1386
|
+
return join(this.props.dir, `${urn}.log.jsonl`);
|
|
1387
|
+
}
|
|
1388
|
+
async mkdir() {
|
|
1389
|
+
await mkdir(this.props.dir, { recursive: true });
|
|
1390
|
+
}
|
|
1391
|
+
async log(urn, log) {
|
|
1392
|
+
const json = JSON.stringify({
|
|
1393
|
+
user: this.props.user,
|
|
1394
|
+
date: Date.now(),
|
|
1395
|
+
...log
|
|
1396
|
+
});
|
|
1397
|
+
await this.mkdir();
|
|
1398
|
+
await appendFile(this.logFile(urn), `${json}\n`);
|
|
1399
|
+
}
|
|
1400
|
+
async tail(urn, limit = 10) {
|
|
1401
|
+
const file$1 = this.logFile(urn);
|
|
1402
|
+
if (!(await stat(file$1)).isFile()) return [];
|
|
1403
|
+
return (await readFile(file$1, "utf8")).split("\n").filter(Boolean).slice(-limit).map((line) => JSON.parse(line));
|
|
1404
|
+
}
|
|
1405
|
+
};
|
|
1406
|
+
|
|
1297
1407
|
//#endregion
|
|
1298
1408
|
//#region src/backend/file/state.ts
|
|
1299
1409
|
const debug = createDebugger("State");
|
|
@@ -1302,7 +1412,7 @@ var FileStateBackend = class {
|
|
|
1302
1412
|
this.props = props;
|
|
1303
1413
|
}
|
|
1304
1414
|
stateFile(urn) {
|
|
1305
|
-
return join(this.props.dir, `${urn}.json`);
|
|
1415
|
+
return join(this.props.dir, `${urn}.state.json`);
|
|
1306
1416
|
}
|
|
1307
1417
|
async mkdir() {
|
|
1308
1418
|
await mkdir(this.props.dir, { recursive: true });
|
|
@@ -1354,40 +1464,35 @@ var FileLockBackend = class {
|
|
|
1354
1464
|
};
|
|
1355
1465
|
|
|
1356
1466
|
//#endregion
|
|
1357
|
-
//#region src/backend/aws/
|
|
1358
|
-
var
|
|
1467
|
+
//#region src/backend/aws/dynamodb-activity-log.ts
|
|
1468
|
+
var DynamoDBActivityLogBackend = class {
|
|
1359
1469
|
client;
|
|
1360
1470
|
constructor(props) {
|
|
1361
1471
|
this.props = props;
|
|
1362
|
-
this.client = new
|
|
1363
|
-
}
|
|
1364
|
-
async get(urn) {
|
|
1365
|
-
let result;
|
|
1366
|
-
try {
|
|
1367
|
-
result = await this.client.send(new GetObjectCommand({
|
|
1368
|
-
Bucket: this.props.bucket,
|
|
1369
|
-
Key: `${urn}.state`
|
|
1370
|
-
}));
|
|
1371
|
-
} catch (error) {
|
|
1372
|
-
if (error instanceof S3ServiceException && error.name === "NoSuchKey") return;
|
|
1373
|
-
throw error;
|
|
1374
|
-
}
|
|
1375
|
-
if (!result.Body) return;
|
|
1376
|
-
const body = await result.Body.transformToString("utf8");
|
|
1377
|
-
return JSON.parse(body);
|
|
1472
|
+
this.client = new DynamoDB(props);
|
|
1378
1473
|
}
|
|
1379
|
-
async
|
|
1380
|
-
await this.client.
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1474
|
+
async log(urn, log) {
|
|
1475
|
+
await this.client.putItem({
|
|
1476
|
+
TableName: this.props.tableName,
|
|
1477
|
+
Item: marshall({
|
|
1478
|
+
urn,
|
|
1479
|
+
user: this.props.user,
|
|
1480
|
+
date: Date.now(),
|
|
1481
|
+
...log
|
|
1482
|
+
})
|
|
1483
|
+
});
|
|
1385
1484
|
}
|
|
1386
|
-
async
|
|
1387
|
-
await this.client.
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1485
|
+
async tail(urn, limit = 10) {
|
|
1486
|
+
return (await this.client.query({
|
|
1487
|
+
TableName: this.props.tableName,
|
|
1488
|
+
KeyConditionExpression: "#urn = :urn",
|
|
1489
|
+
ExpressionAttributeNames: { "#urn": "urn" },
|
|
1490
|
+
ExpressionAttributeValues: { ":urn": marshall(urn) },
|
|
1491
|
+
ScanIndexForward: false,
|
|
1492
|
+
Limit: limit
|
|
1493
|
+
})).Items?.map((item) => {
|
|
1494
|
+
return unmarshall(item);
|
|
1495
|
+
}) ?? [];
|
|
1391
1496
|
}
|
|
1392
1497
|
};
|
|
1393
1498
|
|
|
@@ -1438,6 +1543,44 @@ var DynamoLockBackend = class {
|
|
|
1438
1543
|
}
|
|
1439
1544
|
};
|
|
1440
1545
|
|
|
1546
|
+
//#endregion
|
|
1547
|
+
//#region src/backend/aws/s3-state.ts
|
|
1548
|
+
var S3StateBackend = class {
|
|
1549
|
+
client;
|
|
1550
|
+
constructor(props) {
|
|
1551
|
+
this.props = props;
|
|
1552
|
+
this.client = new S3Client(props);
|
|
1553
|
+
}
|
|
1554
|
+
async get(urn) {
|
|
1555
|
+
let result;
|
|
1556
|
+
try {
|
|
1557
|
+
result = await this.client.send(new GetObjectCommand({
|
|
1558
|
+
Bucket: this.props.bucket,
|
|
1559
|
+
Key: `${urn}.state`
|
|
1560
|
+
}));
|
|
1561
|
+
} catch (error) {
|
|
1562
|
+
if (error instanceof S3ServiceException && error.name === "NoSuchKey") return;
|
|
1563
|
+
throw error;
|
|
1564
|
+
}
|
|
1565
|
+
if (!result.Body) return;
|
|
1566
|
+
const body = await result.Body.transformToString("utf8");
|
|
1567
|
+
return JSON.parse(body);
|
|
1568
|
+
}
|
|
1569
|
+
async update(urn, state) {
|
|
1570
|
+
await this.client.send(new PutObjectCommand({
|
|
1571
|
+
Bucket: this.props.bucket,
|
|
1572
|
+
Key: `${urn}.state`,
|
|
1573
|
+
Body: JSON.stringify(state)
|
|
1574
|
+
}));
|
|
1575
|
+
}
|
|
1576
|
+
async delete(urn) {
|
|
1577
|
+
await this.client.send(new DeleteObjectCommand({
|
|
1578
|
+
Bucket: this.props.bucket,
|
|
1579
|
+
Key: `${urn}.state`
|
|
1580
|
+
}));
|
|
1581
|
+
}
|
|
1582
|
+
};
|
|
1583
|
+
|
|
1441
1584
|
//#endregion
|
|
1442
1585
|
//#region src/helpers.ts
|
|
1443
1586
|
const file = (path, encoding = "utf8") => {
|
|
@@ -1548,4 +1691,4 @@ const createCustomProvider = (providerId, resourceProviders) => {
|
|
|
1548
1691
|
};
|
|
1549
1692
|
|
|
1550
1693
|
//#endregion
|
|
1551
|
-
export { App, AppError, DynamoLockBackend, FileLockBackend, FileStateBackend, Future, Group, MemoryLockBackend, MemoryStateBackend, Output, ResourceAlreadyExists, ResourceError, ResourceNotFound, S3StateBackend, Stack, WorkSpace, createCustomProvider, createCustomResourceClass, createDebugger, createMeta, deferredOutput, enableDebug, findInputDeps, getMeta, isDataSource, isNode, isResource, nodeMetaSymbol, output, resolveInputs };
|
|
1694
|
+
export { App, AppError, DynamoDBActivityLogBackend, DynamoLockBackend, FileActivityLogBackend, FileLockBackend, FileStateBackend, Future, Group, MemoryActivityLogBackend, MemoryLockBackend, MemoryStateBackend, Output, ResourceAlreadyExists, ResourceError, ResourceNotFound, S3StateBackend, Stack, WorkSpace, createCustomProvider, createCustomResourceClass, createDebugger, createMeta, deferredOutput, enableDebug, findInputDeps, getMeta, isDataSource, isNode, isResource, nodeMetaSymbol, output, resolveInputs };
|