@terraforge/core 0.0.18 → 0.0.19

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 CHANGED
@@ -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;
@@ -338,7 +344,7 @@ declare class WorkSpace {
338
344
  /**
339
345
  * Get the status of all resources in the app by comparing current config with state file.
340
346
  */
341
- status(app: App): Promise<ResourceStatusInfo[]>;
347
+ status(app: App): Promise<StackStatusInfo[]>;
342
348
  protected destroyProviders(): Promise<void>;
343
349
  }
344
350
  //#endregion
@@ -465,4 +471,4 @@ type CustomResourceProvider = Partial<{
465
471
  }>;
466
472
  declare const createCustomProvider: (providerId: string, resourceProviders: Record<string, CustomResourceProvider>) => Provider;
467
473
  //#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 };
474
+ 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 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
@@ -623,6 +623,16 @@ const deleteApp = async (app, opt) => {
623
623
  delete stackState.nodes[urn];
624
624
  });
625
625
  const errors = await graph.run();
626
+ if (errors.length === 0 && appState.pendingDeletes) {
627
+ for (const [urn, nodeState] of entries(appState.pendingDeletes)) try {
628
+ await deleteResource(appState.idempotentToken, urn, nodeState, opt);
629
+ delete appState.pendingDeletes[urn];
630
+ } catch (error) {
631
+ if (error instanceof Error) errors.push(error);
632
+ else errors.push(/* @__PURE__ */ new Error(`${error}`));
633
+ }
634
+ if (Object.keys(appState.pendingDeletes).length === 0) delete appState.pendingDeletes;
635
+ }
626
636
  removeEmptyStackStates(appState);
627
637
  delete appState.idempotentToken;
628
638
  await opt.backend.state.update(app.urn, appState);
@@ -813,7 +823,8 @@ const updateResource = async (resource, appToken, priorInputState, priorOutputSt
813
823
  const idempotantToken = createIdempotantToken(appToken, meta.urn, "update");
814
824
  let result;
815
825
  debug$2(meta.type);
816
- debug$2(proposedState);
826
+ debug$2("prior state", priorOutputState);
827
+ debug$2("proposed state", proposedState);
817
828
  try {
818
829
  await opt.hooks?.beforeResourceUpdate?.({
819
830
  urn: resource.urn,
@@ -883,7 +894,6 @@ const deployApp = async (app, opt) => {
883
894
  }
884
895
  const queue = createConcurrencyQueue(opt.concurrency ?? 10);
885
896
  const graph = new DependencyGraph();
886
- const replacementDeletes = /* @__PURE__ */ new Map();
887
897
  const allNodes = {};
888
898
  for (const stackState of Object.values(appState.stacks)) for (const [urn, nodeState] of entries(stackState.nodes)) allNodes[urn] = nodeState;
889
899
  for (const stack of filteredOutStacks) {
@@ -966,10 +976,33 @@ const deployApp = async (app, opt) => {
966
976
  let newResourceState;
967
977
  const ignoreReplace = forcedUpdateDependents.has(meta$1.urn);
968
978
  if (!ignoreReplace && requiresReplacement(nodeState.input, input, meta$1.config?.replaceOnChanges ?? [])) if (meta$1.config?.createBeforeReplace) {
979
+ for (const [dependentUrn, dependentNode] of nodeByUrn.entries()) {
980
+ if (!isResource(dependentNode)) continue;
981
+ const dependentMeta = getMeta(dependentNode);
982
+ if (!dependentMeta.dependencies.has(meta$1.urn)) continue;
983
+ const dependentStackState = stackStates.get(dependentMeta.stack.urn);
984
+ const dependentState = dependentStackState?.nodes[dependentUrn];
985
+ if (!dependentStackState || !dependentState) continue;
986
+ const dependencyPaths = findDependencyPaths(dependentMeta.input, meta$1.urn);
987
+ if (dependencyPaths.length === 0) continue;
988
+ const dependentProvider = findProvider(opt.providers, dependentMeta.provider);
989
+ if (dependentProvider.planResourceChange) {
990
+ if ((await dependentProvider.planResourceChange({
991
+ type: dependentMeta.type,
992
+ priorState: dependentState.output,
993
+ proposedState: input
994
+ })).requiresReplacement) {
995
+ 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.`));
996
+ }
997
+ }
998
+ }
969
999
  const priorState = { ...nodeState };
970
1000
  newResourceState = await createResource(node, appState.idempotentToken, input, opt);
971
1001
  if (newResourceState.output) meta$1.resolve(newResourceState.output);
972
- if (!meta$1.config?.retainOnDelete) replacementDeletes.set(meta$1.urn, priorState);
1002
+ if (!meta$1.config?.retainOnDelete) {
1003
+ appState.pendingDeletes ??= {};
1004
+ appState.pendingDeletes[meta$1.urn] = priorState;
1005
+ }
973
1006
  } else {
974
1007
  for (const [dependentUrn, dependentNode] of nodeByUrn.entries()) {
975
1008
  if (!isResource(dependentNode)) continue;
@@ -1028,11 +1061,15 @@ const deployApp = async (app, opt) => {
1028
1061
  }
1029
1062
  }
1030
1063
  const errors = await graph.run();
1031
- if (errors.length === 0 && replacementDeletes.size > 0) for (const [urn, nodeState] of replacementDeletes.entries()) try {
1032
- await deleteResource(appState.idempotentToken, urn, nodeState, opt);
1033
- } catch (error) {
1034
- if (error instanceof Error) errors.push(error);
1035
- else errors.push(/* @__PURE__ */ new Error(`${error}`));
1064
+ if (errors.length === 0 && appState.pendingDeletes) {
1065
+ for (const [urn, nodeState] of entries(appState.pendingDeletes)) try {
1066
+ await deleteResource(appState.idempotentToken, urn, nodeState, opt);
1067
+ delete appState.pendingDeletes[urn];
1068
+ } catch (error) {
1069
+ if (error instanceof Error) errors.push(error);
1070
+ else errors.push(/* @__PURE__ */ new Error(`${error}`));
1071
+ }
1072
+ if (Object.keys(appState.pendingDeletes).length === 0) delete appState.pendingDeletes;
1036
1073
  }
1037
1074
  removeEmptyStackStates(appState);
1038
1075
  delete appState.idempotentToken;
@@ -1135,11 +1172,12 @@ const filterStateToMatchConfig = (state, config) => {
1135
1172
  };
1136
1173
  const status = async (app, opt) => {
1137
1174
  const appState = await opt.backend.state.get(app.urn);
1138
- const resources = [];
1175
+ const stacks = [];
1139
1176
  const configuredUrns = /* @__PURE__ */ new Set();
1140
1177
  for (const stack of app.stacks) for (const node of stack.nodes) configuredUrns.add(getMeta(node).urn);
1141
1178
  for (const stack of app.stacks) {
1142
1179
  const stackState = appState?.stacks[stack.urn];
1180
+ const resources = [];
1143
1181
  for (const node of stack.nodes) {
1144
1182
  const meta = getMeta(node);
1145
1183
  const nodeState = stackState?.nodes[meta.urn];
@@ -1172,18 +1210,31 @@ const status = async (app, opt) => {
1172
1210
  status: "stale"
1173
1211
  });
1174
1212
  }
1213
+ stacks.push({
1214
+ name: stack.name,
1215
+ urn: stack.urn,
1216
+ resources
1217
+ });
1175
1218
  }
1176
1219
  if (appState) {
1177
1220
  const configuredStackUrns = new Set(app.stacks.map((s) => s.urn));
1178
- for (const [stackUrn, stackState] of Object.entries(appState.stacks)) if (!configuredStackUrns.has(stackUrn)) for (const [urn, nodeState] of Object.entries(stackState.nodes)) resources.push({
1179
- urn,
1180
- type: nodeState.type,
1181
- provider: nodeState.provider,
1182
- tag: nodeState.tag,
1183
- status: "stale"
1184
- });
1221
+ for (const [stackUrn, stackState] of Object.entries(appState.stacks)) if (!configuredStackUrns.has(stackUrn)) {
1222
+ const resources = [];
1223
+ for (const [urn, nodeState] of Object.entries(stackState.nodes)) resources.push({
1224
+ urn,
1225
+ type: nodeState.type,
1226
+ provider: nodeState.provider,
1227
+ tag: nodeState.tag,
1228
+ status: "stale"
1229
+ });
1230
+ stacks.push({
1231
+ name: stackState.name,
1232
+ urn: stackUrn,
1233
+ resources
1234
+ });
1235
+ }
1185
1236
  }
1186
- return resources;
1237
+ return stacks;
1187
1238
  };
1188
1239
 
1189
1240
  //#endregion
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@terraforge/core",
3
- "version": "0.0.18",
3
+ "version": "0.0.19",
4
4
  "type": "module",
5
5
  "repository": {
6
6
  "type": "git",