declastruct 1.2.0 → 1.3.1

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.
Files changed (59) hide show
  1. package/dist/domain.objects/ContextDeclastruct.d.ts +1 -1
  2. package/dist/domain.objects/DeclastructChange.d.ts +1 -1
  3. package/dist/domain.objects/DeclastructChange.js.map +1 -1
  4. package/dist/domain.objects/DeclastructDao.d.ts +2 -2
  5. package/dist/domain.objects/DeclastructPlan.d.ts +2 -2
  6. package/dist/domain.objects/DeclastructProvider.d.ts +1 -1
  7. package/dist/domain.operations/apply/applyChanges.js +8 -3
  8. package/dist/domain.operations/apply/applyChanges.js.map +1 -1
  9. package/dist/domain.operations/plan/computeChange.js +2 -28
  10. package/dist/domain.operations/plan/computeChange.js.map +1 -1
  11. package/dist/domain.operations/plan/getDisplayableDiff.d.ts +10 -0
  12. package/dist/domain.operations/plan/getDisplayableDiff.js +40 -0
  13. package/dist/domain.operations/plan/getDisplayableDiff.js.map +1 -0
  14. package/dist/infra/asIsoTimestamp.d.ts +1 -1
  15. package/license.md +21 -0
  16. package/package.json +19 -9
  17. package/readme.md +52 -32
  18. package/dist/.test/assets/providers/demo.provider.d.ts +0 -32
  19. package/dist/.test/assets/providers/demo.provider.js +0 -101
  20. package/dist/.test/assets/providers/demo.provider.js.map +0 -1
  21. package/dist/contract/cli/apply.integration.test.d.ts +0 -1
  22. package/dist/contract/cli/apply.integration.test.js +0 -215
  23. package/dist/contract/cli/apply.integration.test.js.map +0 -1
  24. package/dist/contract/cli/plan.integration.test.d.ts +0 -1
  25. package/dist/contract/cli/plan.integration.test.js +0 -142
  26. package/dist/contract/cli/plan.integration.test.js.map +0 -1
  27. package/dist/domain.objects/ContextDeclastruct.test.d.ts +0 -1
  28. package/dist/domain.objects/ContextDeclastruct.test.js +0 -41
  29. package/dist/domain.objects/ContextDeclastruct.test.js.map +0 -1
  30. package/dist/domain.objects/DeclastructChange.test.d.ts +0 -1
  31. package/dist/domain.objects/DeclastructChange.test.js +0 -59
  32. package/dist/domain.objects/DeclastructChange.test.js.map +0 -1
  33. package/dist/domain.objects/DeclastructDao.test.d.ts +0 -1
  34. package/dist/domain.objects/DeclastructDao.test.js +0 -78
  35. package/dist/domain.objects/DeclastructDao.test.js.map +0 -1
  36. package/dist/domain.objects/DeclastructPlan.test.d.ts +0 -1
  37. package/dist/domain.objects/DeclastructPlan.test.js +0 -31
  38. package/dist/domain.objects/DeclastructPlan.test.js.map +0 -1
  39. package/dist/domain.objects/DeclastructProvider.test.d.ts +0 -1
  40. package/dist/domain.objects/DeclastructProvider.test.js +0 -80
  41. package/dist/domain.objects/DeclastructProvider.test.js.map +0 -1
  42. package/dist/domain.objects/IsoTimestamp.test.d.ts +0 -1
  43. package/dist/domain.objects/IsoTimestamp.test.js +0 -10
  44. package/dist/domain.objects/IsoTimestamp.test.js.map +0 -1
  45. package/dist/domain.operations/apply/applyChange.test.d.ts +0 -1
  46. package/dist/domain.operations/apply/applyChange.test.js +0 -241
  47. package/dist/domain.operations/apply/applyChange.test.js.map +0 -1
  48. package/dist/domain.operations/apply/applyChanges.integration.test.d.ts +0 -1
  49. package/dist/domain.operations/apply/applyChanges.integration.test.js +0 -291
  50. package/dist/domain.operations/apply/applyChanges.integration.test.js.map +0 -1
  51. package/dist/domain.operations/plan/computeChange.test.d.ts +0 -1
  52. package/dist/domain.operations/plan/computeChange.test.js +0 -153
  53. package/dist/domain.operations/plan/computeChange.test.js.map +0 -1
  54. package/dist/domain.operations/plan/getDaoByResource.test.d.ts +0 -1
  55. package/dist/domain.operations/plan/getDaoByResource.test.js +0 -101
  56. package/dist/domain.operations/plan/getDaoByResource.test.js.map +0 -1
  57. package/dist/domain.operations/plan/planChanges.integration.test.d.ts +0 -1
  58. package/dist/domain.operations/plan/planChanges.integration.test.js +0 -200
  59. package/dist/domain.operations/plan/planChanges.integration.test.js.map +0 -1
@@ -1,4 +1,4 @@
1
- import Bottleneck from 'bottleneck';
1
+ import type Bottleneck from 'bottleneck';
2
2
  /**
3
3
  * .what = standard context for all declastruct operations
4
4
  * .why = provides concurrency control and log trail
@@ -1,4 +1,4 @@
1
- import { DomainEntity, DomainLiteral } from 'domain-objects';
1
+ import { type DomainEntity, DomainLiteral } from 'domain-objects';
2
2
  /**
3
3
  * .what = actions that can be proposed for a resource
4
4
  * .why = clearly defines the type of change declastruct will execute
@@ -1 +1 @@
1
- {"version":3,"file":"DeclastructChange.js","sourceRoot":"","sources":["../../src/domain.objects/DeclastructChange.ts"],"names":[],"mappings":";;;AAAA,mDAA6D;AAE7D;;;GAGG;AACH,IAAY,uBAyBX;AAzBD,WAAY,uBAAuB;IACjC;;OAEG;IACH,wCAAa,CAAA;IAEb;;OAEG;IACH,4CAAiB,CAAA;IAEjB;;OAEG;IACH,4CAAiB,CAAA;IAEjB;;OAEG;IACH,8CAAmB,CAAA;IAEnB;;OAEG;IACH,8CAAmB,CAAA;AACrB,CAAC,EAzBW,uBAAuB,uCAAvB,uBAAuB,QAyBlC;AAsDD,MAAa,iBAGX,SAAQ,8BAA2C;CACT;AAJ5C,8CAI4C"}
1
+ {"version":3,"file":"DeclastructChange.js","sourceRoot":"","sources":["../../src/domain.objects/DeclastructChange.ts"],"names":[],"mappings":";;;AAAA,mDAAkE;AAElE;;;GAGG;AACH,IAAY,uBAyBX;AAzBD,WAAY,uBAAuB;IACjC;;OAEG;IACH,wCAAa,CAAA;IAEb;;OAEG;IACH,4CAAiB,CAAA;IAEjB;;OAEG;IACH,4CAAiB,CAAA;IAEjB;;OAEG;IACH,8CAAmB,CAAA;IAEnB;;OAEG;IACH,8CAAmB,CAAA;AACrB,CAAC,EAzBW,uBAAuB,uCAAvB,uBAAuB,QAyBlC;AAsDD,MAAa,iBAGX,SAAQ,8BAA2C;CACT;AAJ5C,8CAI4C"}
@@ -1,5 +1,5 @@
1
- import { DomainEntity, DomainLiteral, Ref, Refable, RefByPrimary, RefByUnique } from 'domain-objects';
2
- import { HasMetadata } from 'type-fns';
1
+ import { type DomainEntity, DomainLiteral, type Ref, type Refable, type RefByPrimary, type RefByUnique } from 'domain-objects';
2
+ import { type HasMetadata } from 'type-fns';
3
3
  /**
4
4
  * .what = standardized data access interface for any resource type
5
5
  *
@@ -1,6 +1,6 @@
1
1
  import { DomainEntity } from 'domain-objects';
2
- import { DeclastructChange } from './DeclastructChange';
3
- import { IsoTimestamp } from './IsoTimestamp';
2
+ import { type DeclastructChange } from './DeclastructChange';
3
+ import { type IsoTimestamp } from './IsoTimestamp';
4
4
  /**
5
5
  * .what = collection of all planned changes required to fulfill a wish
6
6
  * .why = enables review, version control, and validation of infrastructure changes
@@ -1,5 +1,5 @@
1
1
  import { DomainLiteral } from 'domain-objects';
2
- import { DeclastructDao } from './DeclastructDao';
2
+ import { type DeclastructDao } from './DeclastructDao';
3
3
  export type DeclastructDaosShape<TContext> = Record<string, DeclastructDao<any, any, TContext>>;
4
4
  /**
5
5
  * .what = bundles all DAOs and lifecycle hooks for a specific infrastructure provider
@@ -52,14 +52,19 @@ const applyChanges = async (input, context) => {
52
52
  const resourceFound = input.resources.find((candidate) => candidate.constructor.name === change.forResource.class &&
53
53
  (0, domain_objects_1.getUniqueIdentifierSlug)(candidate) === change.forResource.slug) ??
54
54
  helpful_errors_1.UnexpectedCodePathError.throw('could not find resource specified in plan. was it removed?', { change });
55
- // apply the change
55
+ // log action start
56
+ context.log.info(`○ [${change.action}] ${change.forResource.slug}`, {});
57
+ // apply the change with timing
58
+ const startMs = Date.now();
56
59
  const applied = await (0, applyChange_1.applyChange)({
57
60
  change,
58
61
  resource: resourceFound,
59
62
  providers: input.providers,
60
63
  });
61
- // log success immediately
62
- context.log.info(`✔ [${applied.action}] ${applied.forResource.slug}`, {});
64
+ const durationMs = Date.now() - startMs;
65
+ // log completion with duration
66
+ const durationSec = (durationMs / 1000).toFixed(2);
67
+ context.log.info(` └─ ✔ done in ${durationSec}s`, {});
63
68
  appliedChanges.push(applied);
64
69
  }
65
70
  return { appliedChanges };
@@ -1 +1 @@
1
- {"version":3,"file":"applyChanges.js","sourceRoot":"","sources":["../../../src/domain.operations/apply/applyChanges.ts"],"names":[],"mappings":";;;AAAA,mDAAuE;AACvE,mDAAyD;AAIzD,8EAGgD;AAGhD,qDAAkD;AAClD,+CAAwD;AACxD,+CAA4C;AAE5C;;;;;;GAMG;AACI,MAAM,YAAY,GAAG,KAAK,EAC/B,KAIC,EACD,OAA6C,EACK,EAAE;IACpD,8BAA8B;IAC9B,MAAM,WAAW,GAAG,MAAM,IAAA,yBAAW,EACnC;QACE,SAAS,EAAE,KAAK,CAAC,SAAS;QAC1B,SAAS,EAAE,KAAK,CAAC,SAAS;QAC1B,YAAY,EAAE,KAAK,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG,IAAI,WAAW;KAClD,EACD,OAAO,CACR,CAAC;IAEF,iFAAiF;IACjF,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC;QACf,IAAA,+BAAoB,EAAC;YACnB,YAAY,EAAE,KAAK,CAAC,IAAI;YACxB,WAAW;SACZ,CAAC,CAAC;QACH,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACrB,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,wBAAwB,CAAC,CAAC;IAC7C,CAAC;SAAM,CAAC;QACN,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACrB,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,+BAA+B,CAAC,CAAC;IACpD,CAAC;IAED,oDAAoD;IACpD,MAAM,WAAW,GAAG,WAAW,CAAC;IAEhC,yBAAyB;IACzB,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACrB,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,sBAAsB,CAAC,CAAC;IACzC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAErB,2CAA2C;IAC3C,MAAM,cAAc,GAAwB,EAAE,CAAC;IAE/C,KAAK,MAAM,MAAM,IAAI,WAAW,CAAC,OAAO,EAAE,CAAC;QACzC,4BAA4B;QAC5B,IAAI,MAAM,CAAC,MAAM,KAAK,2CAAuB,CAAC,IAAI,EAAE,CAAC;YACnD,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,YAAY,MAAM,CAAC,WAAW,CAAC,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC;YAC5D,SAAS;QACX,CAAC;QAED,4BAA4B;QAC5B,MAAM,aAAa,GACjB,KAAK,CAAC,SAAS,CAAC,IAAI,CAClB,CAAC,SAAS,EAAE,EAAE,CACZ,SAAS,CAAC,WAAW,CAAC,IAAI,KAAK,MAAM,CAAC,WAAW,CAAC,KAAK;YACvD,IAAA,wCAAuB,EAAC,SAAS,CAAC,KAAK,MAAM,CAAC,WAAW,CAAC,IAAI,CACjE;YACD,wCAAuB,CAAC,KAAK,CAC3B,4DAA4D,EAC5D,EAAE,MAAM,EAAE,CACX,CAAC;QAEJ,mBAAmB;QACnB,MAAM,OAAO,GAAG,MAAM,IAAA,yBAAW,EAAC;YAChC,MAAM;YACN,QAAQ,EAAE,aAAa;YACvB,SAAS,EAAE,KAAK,CAAC,SAAS;SAC3B,CAAC,CAAC;QAEH,0BAA0B;QAC1B,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,OAAO,CAAC,MAAM,KAAK,OAAO,CAAC,WAAW,CAAC,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC;QAE1E,cAAc,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAC/B,CAAC;IAED,OAAO,EAAE,cAAc,EAAE,CAAC;AAC5B,CAAC,CAAC;AA3EW,QAAA,YAAY,gBA2EvB"}
1
+ {"version":3,"file":"applyChanges.js","sourceRoot":"","sources":["../../../src/domain.operations/apply/applyChanges.ts"],"names":[],"mappings":"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"}
@@ -3,8 +3,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.computeChange = void 0;
4
4
  const domain_objects_1 = require("domain-objects");
5
5
  const helpful_errors_1 = require("helpful-errors");
6
- const jest_diff_1 = require("jest-diff");
7
6
  const DeclastructChange_1 = require("../../domain.objects/DeclastructChange");
7
+ const getDisplayableDiff_1 = require("./getDisplayableDiff");
8
8
  /**
9
9
  * .what = checks if two resources are equivalent
10
10
  * .why = determines whether a resource needs to be updated
@@ -16,32 +16,6 @@ const checkAreResourcesEquivalent = (input) => {
16
16
  const desiredSerialized = (0, domain_objects_1.serialize)((0, domain_objects_1.omitReadonly)(input.desired));
17
17
  return remoteSerialized === desiredSerialized;
18
18
  };
19
- /**
20
- * .what = computes human-readable diff between two resources
21
- * .why = helps users understand what will change
22
- * .note = returns null if resources are identical; for CREATE uses empty object to show all attributes; ignores readonly
23
- */
24
- const computeDiff = ({ from, into, }) => {
25
- // no diff if both are null
26
- if (from === null && into === null)
27
- return null;
28
- // check if resources are equivalent after omitting readonly
29
- if (from !== null && into !== null) {
30
- const fromSerialized = (0, domain_objects_1.serialize)((0, domain_objects_1.omitReadonly)(from));
31
- const intoSerialized = (0, domain_objects_1.serialize)((0, domain_objects_1.omitReadonly)(into));
32
- if (fromSerialized === intoSerialized)
33
- return null;
34
- }
35
- // omit readonly before diff
36
- const fromWithoutReadonly = from === null ? {} : (0, domain_objects_1.omitReadonly)(from);
37
- const intoWithoutReadonly = into === null ? {} : (0, domain_objects_1.omitReadonly)(into);
38
- // compute diff using jest-diff
39
- const difference = (0, jest_diff_1.diff)(fromWithoutReadonly, intoWithoutReadonly, {
40
- aAnnotation: 'Remote',
41
- bAnnotation: 'Desired',
42
- });
43
- return difference;
44
- };
45
19
  /**
46
20
  * .what = computes a single change by comparing desired vs remote state
47
21
  * .why = determines the action needed to achieve desired state for one resource
@@ -75,7 +49,7 @@ const computeChange = ({ desired, remote, }) => {
75
49
  // compute displayable difference
76
50
  const difference = action === DeclastructChange_1.DeclastructChangeAction.KEEP
77
51
  ? null
78
- : computeDiff({ from: remote, into: desired });
52
+ : (0, getDisplayableDiff_1.getDisplayableDiff)({ from: remote, into: desired });
79
53
  // get resource info for change record
80
54
  const resourceForChange = desired || remote;
81
55
  // return change
@@ -1 +1 @@
1
- {"version":3,"file":"computeChange.js","sourceRoot":"","sources":["../../../src/domain.operations/plan/computeChange.ts"],"names":[],"mappings":";;;AAAA,mDAKwB;AACxB,mDAAyD;AACzD,yCAAiC;AAEjC,8EAGgD;AAEhD;;;;GAIG;AACH,MAAM,2BAA2B,GAAG,CAAC,KAGpC,EAAW,EAAE;IACZ,0EAA0E;IAC1E,MAAM,gBAAgB,GAAG,IAAA,0BAAS,EAAC,IAAA,6BAAY,EAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC;IAC/D,MAAM,iBAAiB,GAAG,IAAA,0BAAS,EAAC,IAAA,6BAAY,EAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC;IAEjE,OAAO,gBAAgB,KAAK,iBAAiB,CAAC;AAChD,CAAC,CAAC;AAEF;;;;GAIG;AACH,MAAM,WAAW,GAAG,CAAC,EACnB,IAAI,EACJ,IAAI,GAIL,EAAiB,EAAE;IAClB,2BAA2B;IAC3B,IAAI,IAAI,KAAK,IAAI,IAAI,IAAI,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IAEhD,4DAA4D;IAC5D,IAAI,IAAI,KAAK,IAAI,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;QACnC,MAAM,cAAc,GAAG,IAAA,0BAAS,EAAC,IAAA,6BAAY,EAAC,IAAI,CAAC,CAAC,CAAC;QACrD,MAAM,cAAc,GAAG,IAAA,0BAAS,EAAC,IAAA,6BAAY,EAAC,IAAI,CAAC,CAAC,CAAC;QACrD,IAAI,cAAc,KAAK,cAAc;YAAE,OAAO,IAAI,CAAC;IACrD,CAAC;IAED,4BAA4B;IAC5B,MAAM,mBAAmB,GAAG,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAA,6BAAY,EAAC,IAAI,CAAC,CAAC;IACpE,MAAM,mBAAmB,GAAG,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAA,6BAAY,EAAC,IAAI,CAAC,CAAC;IAEpE,+BAA+B;IAC/B,MAAM,UAAU,GAAG,IAAA,gBAAI,EAAC,mBAAmB,EAAE,mBAAmB,EAAE;QAChE,WAAW,EAAE,QAAQ;QACrB,WAAW,EAAE,SAAS;KACvB,CAAC,CAAC;IAEH,OAAO,UAAU,CAAC;AACpB,CAAC,CAAC;AAEF;;;;GAIG;AACI,MAAM,aAAa,GAAG,CAAC,EAC5B,OAAO,EACP,MAAM,GAIP,EAAqB,EAAE;IACtB,6CAA6C;IAC7C,MAAM,MAAM,GAAG,CAAC,GAAG,EAAE;QACnB,kCAAkC;QAClC,IAAI,CAAC,MAAM;YAAE,OAAO,2CAAuB,CAAC,MAAM,CAAC;QAEnD,wCAAwC;QACxC,IAAI,CAAC,OAAO;YAAE,OAAO,2CAAuB,CAAC,OAAO,CAAC;QAErD,oBAAoB;QACpB,MAAM,sBAAsB,GAAG,wCAAuB,CAAC,IAAI,CACzD,GAAG,EAAE,CAAC,2BAA2B,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,EACtD;YACE,OAAO,EAAE,uCAAuC;YAChD,QAAQ,EAAE;gBACR,KAAK,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE;gBAC1B,KAAK,EAAE;oBACL,OAAO,EAAE,OAAO,EAAE,WAAW,CAAC,IAAI;oBAClC,MAAM,EAAE,MAAM,EAAE,WAAW,CAAC,IAAI;iBACjC;aACF;SACF,CACF,EAAE,CAAC;QACJ,IAAI,sBAAsB;YAAE,OAAO,2CAAuB,CAAC,IAAI,CAAC;QAEhE,qCAAqC;QACrC,OAAO,2CAAuB,CAAC,MAAM,CAAC;IACxC,CAAC,CAAC,EAAE,CAAC;IAEL,iCAAiC;IACjC,MAAM,UAAU,GACd,MAAM,KAAK,2CAAuB,CAAC,IAAI;QACrC,CAAC,CAAC,IAAI;QACN,CAAC,CAAC,WAAW,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC;IAEnD,sCAAsC;IACtC,MAAM,iBAAiB,GAAG,OAAO,IAAI,MAAO,CAAC;IAE7C,gBAAgB;IAChB,OAAO,IAAI,qCAAiB,CAAC;QAC3B,WAAW,EAAE;YACX,KAAK,EAAE,iBAAiB,CAAC,WAAW,CAAC,IAAI;YACzC,IAAI,EAAE,wCAAuB,CAAC,IAAI,CAChC,GAAG,EAAE,CAAC,IAAA,wCAAuB,EAAC,iBAAiB,CAAC,EAChD;gBACE,OAAO,EAAE,mCAAmC;gBAC5C,QAAQ,EAAE;oBACR,KAAK,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE;oBAC1B,KAAK,EAAE;wBACL,OAAO,EAAE,OAAO,EAAE,WAAW,CAAC,IAAI;wBAClC,MAAM,EAAE,MAAM,EAAE,WAAW,CAAC,IAAI;qBACjC;iBACF;aACF,CACF,EAAE;SACJ;QACD,MAAM;QACN,KAAK,EAAE;YACL,OAAO;YACP,MAAM;YACN,UAAU;SACX;KACF,CAAC,CAAC;AACL,CAAC,CAAC;AArEW,QAAA,aAAa,iBAqExB"}
1
+ {"version":3,"file":"computeChange.js","sourceRoot":"","sources":["../../../src/domain.operations/plan/computeChange.ts"],"names":[],"mappings":";;;AAAA,mDAKwB;AACxB,mDAAyD;AAEzD,8EAGgD;AAChD,6DAA0D;AAE1D;;;;GAIG;AACH,MAAM,2BAA2B,GAAG,CAAC,KAGpC,EAAW,EAAE;IACZ,0EAA0E;IAC1E,MAAM,gBAAgB,GAAG,IAAA,0BAAS,EAAC,IAAA,6BAAY,EAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC;IAC/D,MAAM,iBAAiB,GAAG,IAAA,0BAAS,EAAC,IAAA,6BAAY,EAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC;IAEjE,OAAO,gBAAgB,KAAK,iBAAiB,CAAC;AAChD,CAAC,CAAC;AAEF;;;;GAIG;AACI,MAAM,aAAa,GAAG,CAAC,EAC5B,OAAO,EACP,MAAM,GAIP,EAAqB,EAAE;IACtB,6CAA6C;IAC7C,MAAM,MAAM,GAAG,CAAC,GAAG,EAAE;QACnB,kCAAkC;QAClC,IAAI,CAAC,MAAM;YAAE,OAAO,2CAAuB,CAAC,MAAM,CAAC;QAEnD,wCAAwC;QACxC,IAAI,CAAC,OAAO;YAAE,OAAO,2CAAuB,CAAC,OAAO,CAAC;QAErD,oBAAoB;QACpB,MAAM,sBAAsB,GAAG,wCAAuB,CAAC,IAAI,CACzD,GAAG,EAAE,CAAC,2BAA2B,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,EACtD;YACE,OAAO,EAAE,uCAAuC;YAChD,QAAQ,EAAE;gBACR,KAAK,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE;gBAC1B,KAAK,EAAE;oBACL,OAAO,EAAE,OAAO,EAAE,WAAW,CAAC,IAAI;oBAClC,MAAM,EAAE,MAAM,EAAE,WAAW,CAAC,IAAI;iBACjC;aACF;SACF,CACF,EAAE,CAAC;QACJ,IAAI,sBAAsB;YAAE,OAAO,2CAAuB,CAAC,IAAI,CAAC;QAEhE,qCAAqC;QACrC,OAAO,2CAAuB,CAAC,MAAM,CAAC;IACxC,CAAC,CAAC,EAAE,CAAC;IAEL,iCAAiC;IACjC,MAAM,UAAU,GACd,MAAM,KAAK,2CAAuB,CAAC,IAAI;QACrC,CAAC,CAAC,IAAI;QACN,CAAC,CAAC,IAAA,uCAAkB,EAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC;IAE1D,sCAAsC;IACtC,MAAM,iBAAiB,GAAG,OAAO,IAAI,MAAO,CAAC;IAE7C,gBAAgB;IAChB,OAAO,IAAI,qCAAiB,CAAC;QAC3B,WAAW,EAAE;YACX,KAAK,EAAE,iBAAiB,CAAC,WAAW,CAAC,IAAI;YACzC,IAAI,EAAE,wCAAuB,CAAC,IAAI,CAChC,GAAG,EAAE,CAAC,IAAA,wCAAuB,EAAC,iBAAiB,CAAC,EAChD;gBACE,OAAO,EAAE,mCAAmC;gBAC5C,QAAQ,EAAE;oBACR,KAAK,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE;oBAC1B,KAAK,EAAE;wBACL,OAAO,EAAE,OAAO,EAAE,WAAW,CAAC,IAAI;wBAClC,MAAM,EAAE,MAAM,EAAE,WAAW,CAAC,IAAI;qBACjC;iBACF;aACF,CACF,EAAE;SACJ;QACD,MAAM;QACN,KAAK,EAAE;YACL,OAAO;YACP,MAAM;YACN,UAAU;SACX;KACF,CAAC,CAAC;AACL,CAAC,CAAC;AArEW,QAAA,aAAa,iBAqExB"}
@@ -0,0 +1,10 @@
1
+ import { DomainEntity } from 'domain-objects';
2
+ /**
3
+ * .what = computes human-readable diff between two resources
4
+ * .why = helps users understand what will change
5
+ * .note = returns null if resources are identical; for CREATE uses empty object to show all attributes; ignores readonly; preserves key order from into
6
+ */
7
+ export declare const getDisplayableDiff: ({ from, into, }: {
8
+ from: DomainEntity<any> | null;
9
+ into: DomainEntity<any> | null;
10
+ }) => string | null;
@@ -0,0 +1,40 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getDisplayableDiff = void 0;
4
+ const domain_objects_1 = require("domain-objects");
5
+ const jest_diff_1 = require("jest-diff");
6
+ /**
7
+ * .what = computes human-readable diff between two resources
8
+ * .why = helps users understand what will change
9
+ * .note = returns null if resources are identical; for CREATE uses empty object to show all attributes; ignores readonly; preserves key order from into
10
+ */
11
+ const getDisplayableDiff = ({ from, into, }) => {
12
+ // no diff if both are null
13
+ if (from === null && into === null)
14
+ return null;
15
+ // check if resources are equivalent after omitting readonly
16
+ if (from !== null && into !== null) {
17
+ const fromSerialized = (0, domain_objects_1.serialize)((0, domain_objects_1.omitReadonly)(from));
18
+ const intoSerialized = (0, domain_objects_1.serialize)((0, domain_objects_1.omitReadonly)(into));
19
+ if (fromSerialized === intoSerialized)
20
+ return null;
21
+ }
22
+ // omit readonly before diff
23
+ const fromWithoutReadonly = from === null ? {} : (0, domain_objects_1.omitReadonly)(from);
24
+ const intoWithoutReadonly = into === null ? {} : (0, domain_objects_1.omitReadonly)(into);
25
+ // build key order from into (desired) for stable diff output
26
+ const keyOrder = Object.keys(intoWithoutReadonly);
27
+ // compute diff using jest-diff, sorting keys by into's order
28
+ const difference = (0, jest_diff_1.diff)(fromWithoutReadonly, intoWithoutReadonly, {
29
+ aAnnotation: 'Remote',
30
+ bAnnotation: 'Desired',
31
+ compareKeys: (a, b) => {
32
+ const aIdx = keyOrder.indexOf(a);
33
+ const bIdx = keyOrder.indexOf(b);
34
+ return (aIdx === -1 ? Infinity : aIdx) - (bIdx === -1 ? Infinity : bIdx);
35
+ },
36
+ });
37
+ return difference;
38
+ };
39
+ exports.getDisplayableDiff = getDisplayableDiff;
40
+ //# sourceMappingURL=getDisplayableDiff.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"getDisplayableDiff.js","sourceRoot":"","sources":["../../../src/domain.operations/plan/getDisplayableDiff.ts"],"names":[],"mappings":";;;AAAA,mDAAuE;AACvE,yCAAiC;AAEjC;;;;GAIG;AACI,MAAM,kBAAkB,GAAG,CAAC,EACjC,IAAI,EACJ,IAAI,GAIL,EAAiB,EAAE;IAClB,2BAA2B;IAC3B,IAAI,IAAI,KAAK,IAAI,IAAI,IAAI,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IAEhD,4DAA4D;IAC5D,IAAI,IAAI,KAAK,IAAI,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;QACnC,MAAM,cAAc,GAAG,IAAA,0BAAS,EAAC,IAAA,6BAAY,EAAC,IAAI,CAAC,CAAC,CAAC;QACrD,MAAM,cAAc,GAAG,IAAA,0BAAS,EAAC,IAAA,6BAAY,EAAC,IAAI,CAAC,CAAC,CAAC;QACrD,IAAI,cAAc,KAAK,cAAc;YAAE,OAAO,IAAI,CAAC;IACrD,CAAC;IAED,4BAA4B;IAC5B,MAAM,mBAAmB,GAAG,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAA,6BAAY,EAAC,IAAI,CAAC,CAAC;IACpE,MAAM,mBAAmB,GAAG,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAA,6BAAY,EAAC,IAAI,CAAC,CAAC;IAEpE,6DAA6D;IAC7D,MAAM,QAAQ,GAAG,MAAM,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC;IAElD,6DAA6D;IAC7D,MAAM,UAAU,GAAG,IAAA,gBAAI,EAAC,mBAAmB,EAAE,mBAAmB,EAAE;QAChE,WAAW,EAAE,QAAQ;QACrB,WAAW,EAAE,SAAS;QACtB,WAAW,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;YACpB,MAAM,IAAI,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;YACjC,MAAM,IAAI,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;YACjC,OAAO,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QAC3E,CAAC;KACF,CAAC,CAAC;IAEH,OAAO,UAAU,CAAC;AACpB,CAAC,CAAC;AApCW,QAAA,kBAAkB,sBAoC7B"}
@@ -1,4 +1,4 @@
1
- import { IsoTimestamp } from '../domain.objects/IsoTimestamp';
1
+ import { type IsoTimestamp } from '../domain.objects/IsoTimestamp';
2
2
  /**
3
3
  * .what = converts a Date to ISO 8601 timestamp string
4
4
  * .why = ensures consistent timestamp format across the system
package/license.md ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2020 ehmpathy
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "declastruct",
3
3
  "author": "ehmpathy",
4
4
  "description": "Add declarative control to any resource constructs. Declare, plan, and apply within an observable pit-of-success.",
5
- "version": "1.2.0",
5
+ "version": "1.3.1",
6
6
  "repository": "ehmpathy/declastruct",
7
7
  "homepage": "https://github.com/ehmpathy/declastruct",
8
8
  "keywords": [
@@ -38,7 +38,7 @@
38
38
  "test:types": "tsc -p ./tsconfig.build.json --noEmit",
39
39
  "test:format:prettier": "prettier --parser typescript --check 'src/**/*.ts' --config ./prettier.config.js",
40
40
  "test:format": "npm run test:format:prettier",
41
- "test:lint:deps": "npx depcheck -c ./depcheckrc.yml",
41
+ "test:lint:deps": "npx depcheck -c ./.depcheckrc.yml",
42
42
  "test:lint:eslint": "eslint -c ./.eslintrc.js src/**/*.ts",
43
43
  "test:lint": "npm run test:lint:eslint && npm run test:lint:deps",
44
44
  "test:unit": "jest -c ./jest.unit.config.ts --forceExit --verbose --passWithNoTests $([ -z $THOROUGH ] && echo '--changedSince=main')",
@@ -50,8 +50,8 @@
50
50
  "prepublish": "npm run build",
51
51
  "preversion": "npm run prepush",
52
52
  "postversion": "git push origin HEAD --tags --no-verify",
53
- "postinstall": "[ -d .git ] && npm run prepare:husky || exit 0",
54
- "prepare:husky": "npx husky install && chmod ug+x .husky/*"
53
+ "prepare:husky": "npx husky install && chmod ug+x .husky/*",
54
+ "prepare": "[ -d .git ] && npm run prepare:husky || exit 0"
55
55
  },
56
56
  "dependencies": {
57
57
  "bottleneck": "2.19.5",
@@ -60,41 +60,51 @@
60
60
  "jest-diff": "30.0.2",
61
61
  "simple-log-methods": "0.6.2",
62
62
  "tsx": "4.20.6",
63
- "type-fns": "1.19.0",
63
+ "type-fns": "1.21.0",
64
64
  "uuid-fns": "1.0.2"
65
65
  },
66
66
  "peerDependencies": {
67
67
  "domain-objects": "0.31.0"
68
68
  },
69
69
  "devDependencies": {
70
+ "@babel/core": "7.28.5",
71
+ "@babel/preset-env": "7.28.5",
70
72
  "@commitlint/cli": "19.3.0",
71
73
  "@commitlint/config-conventional": "13.1.0",
72
74
  "@trivago/prettier-plugin-sort-imports": "4.3.0",
73
75
  "@tsconfig/node-lts-strictest": "18.12.1",
74
76
  "@types/jest": "29.2.4",
77
+ "@types/node": "22.15.29",
75
78
  "@typescript-eslint/eslint-plugin": "7.8.0",
76
79
  "@typescript-eslint/parser": "7.8.0",
80
+ "babel-jest": "30.2.0",
77
81
  "core-js": "3.26.1",
78
82
  "cz-conventional-changelog": "3.3.0",
79
- "declapract": "0.12.0",
80
- "declapract-typescript-ehmpathy": "0.39.5",
83
+ "declapract": "0.12.3",
84
+ "declapract-typescript-ehmpathy": "0.42.2",
85
+ "declastruct": "1.3.0",
86
+ "declastruct-github": "1.0.3",
81
87
  "depcheck": "1.4.3",
82
88
  "eslint": "8.56.0",
83
89
  "eslint-config-airbnb-typescript": "18.0.0",
84
90
  "eslint-config-prettier": "8.5.0",
85
91
  "eslint-plugin-import": "2.26.0",
86
92
  "eslint-plugin-prettier": "4.2.1",
93
+ "eslint-plugin-unused-imports": "4.1.4",
87
94
  "husky": "8.0.3",
88
95
  "jest": "29.3.1",
89
96
  "prettier": "2.8.1",
90
97
  "rhachet": "1.12.1",
91
98
  "rhachet-roles-ehmpathy": "1.9.1",
92
- "ts-jest": "29.1.3",
99
+ "test-fns": "1.7.2",
100
+ "ts-jest": "29.4.5",
101
+ "tsx": "4.20.6",
93
102
  "typescript": "5.4.5"
94
103
  },
95
104
  "config": {
96
105
  "commitizen": {
97
106
  "path": "./node_modules/cz-conventional-changelog"
98
107
  }
99
- }
108
+ },
109
+ "packageManager": "pnpm@10.24.0"
100
110
  }
package/readme.md CHANGED
@@ -3,20 +3,40 @@
3
3
  ![test](https://github.com/ehmpathy/declastruct/workflows/test/badge.svg)
4
4
  ![publish](https://github.com/ehmpathy/declastruct/workflows/publish/badge.svg)
5
5
 
6
- declarative control for any resource constructs, batteries included
6
+ declarative control of any resource constructs, batteries included
7
7
 
8
8
  # intro
9
9
 
10
- Add declarative control to any resource construct. Declare, plan, and apply within an observable pit-of-success.
10
+ Control any resource construct declaratively. Declare what you want ✨. Plan to see what must change 🔮. Apply to make it so 🪄
11
11
 
12
- Declare the structures you want. Plan to see the changes required. Apply to make it so 🪄
12
+ ## what is it?
13
13
 
14
+ `declastruct` is a framework to control any resource construct via **declarative instructions** — you describe **what** you want the end state to be, and the system figures out **how** to get there.
14
15
 
15
- ## what is it?
16
+ **declarative instructions** means:
17
+ - you declare the desired state of your resources
18
+ - the system compares your desires against reality
19
+ - the system computes the changes required to reconcile reality with your desires
20
+ - you review and apply those changes
16
21
 
17
- `declastruct` is a framework for managing any resource construct declaratively using TypeScript.
22
+ in contrast to **imperative instructions**, where you specify each step and _hope_ it produces what you want:
23
+ ```ts
24
+ // imperative 👎 = you say HOW to do things, step by step
25
+ await createBucket({ name: 'my-bucket' });
26
+ await enableVersioning({ bucket: 'my-bucket' });
27
+ await setEncryption({ bucket: 'my-bucket', type: 'AES256' });
28
+ ```
18
29
 
19
- declare what you want, plan the changes, and apply them safely all without managing separate state files or learning a new language.
30
+ the advantage of **declarative instructions** is that you simply declare what you want and _know_ it will work:
31
+ ```ts
32
+ // declarative 👍 = you say WHAT you want, the system figures out how
33
+ const bucket = DeclaredAwsS3Bucket.as({
34
+ name: 'my-bucket',
35
+ versioning: true,
36
+ encryption: 'AES256',
37
+ });
38
+ await apply({ resources: [bucket] });
39
+ ```
20
40
 
21
41
  works with:
22
42
  - **infrastructure** — AWS, GCP, Azure resources
@@ -26,7 +46,7 @@ works with:
26
46
 
27
47
  think Terraform, but:
28
48
  - **no state files to manage** — compares directly against live remote state
29
- - **no new language to learn** — uses TypeScript and your existing domain objects
49
+ - **no new language to learn** — declare via TypeScript, reuse domain objects and operations
30
50
  - **pit-of-success by default** — enforces idempotency, clear unique keys, and safe operations
31
51
  - **not just infrastructure** — works with any resource construct (saas, databases, apis, etc)
32
52
 
@@ -42,7 +62,7 @@ npm install declastruct --save-dev
42
62
 
43
63
  ### 1. **declare** your desired state ✨
44
64
 
45
- define resource constructs with strongly-typed domain objects:
65
+ declare your wish via strongly-typed [domain-objects](https://github.com/ehmpathy/domain-objects). these are the declarative instructions that will control your resources.
46
66
 
47
67
  ```ts
48
68
  import { getDeclastructAwsProvider, DeclaredAwsS3Bucket } from 'declastruct-aws';
@@ -78,7 +98,7 @@ export const getResources = async () => {
78
98
 
79
99
  ### 2. **plan** the required changes 🔮
80
100
 
81
- see exactly what will change before applying:
101
+ see exactly what must change to make your wish come true, ahead of time
82
102
 
83
103
  ```sh
84
104
  npx declastruct plan \
@@ -102,7 +122,7 @@ planned changes:
102
122
 
103
123
  ### 3. **apply** the plan 🪄
104
124
 
105
- execute the plan to make your desired state reality:
125
+ apply the plan to make your wish come true
106
126
 
107
127
  ```sh
108
128
  npx declastruct apply --plan provision/.temp/plan.json
@@ -116,11 +136,11 @@ npx declastruct apply --plan provision/.temp/plan.json
116
136
 
117
137
  ✅ **type-safe** — full TypeScript support with domain objects
118
138
 
119
- ✅ **idempotent** — safe to run plans multiple times
139
+ ✅ **idempotent** — safe to plan and apply repeatedly
120
140
 
121
- ✅ **observable** — see exactly what will change before applying
141
+ ✅ **observable** — see exactly what would change if you apply
122
142
 
123
- ✅ **composable** — reuse domain objects and operations across application code and resource management
143
+ ✅ **composable** — reuse domain objects and operations across application code and declarative instructions
124
144
 
125
145
  ✅ **pit-of-success** — enforced best practices via idempotent dao interfaces
126
146
 
@@ -131,20 +151,20 @@ npx declastruct apply --plan provision/.temp/plan.json
131
151
 
132
152
  ### no state file management
133
153
 
134
- traditional declarative tools (like Terraform) require maintaining a separate state file that tracks what resources exist. this creates problems:
154
+ traditional declarative tools (like Terraform) require separate state files to track resources existence. this creates problems:
135
155
  - state files can drift from reality
136
- - state locking issues in team environments
156
+ - state lock issues in team environments
137
157
  - state files must be carefully secured and backed up
138
158
 
139
- **declastruct eliminates state files entirely.** it compares your desired resource constructs directly against live remote state using unique keys, so the source of truth is always reality itself.
159
+ **declastruct eliminates state files entirely.** it compares your declared desires directly against live remote state via unique keys, so the source of truth is always reality itself.
140
160
 
141
161
  ### use your existing domain language
142
162
 
143
- instead of learning HCL, YAML, or another DSL:
144
- - declare resource constructs as TypeScript using `domain-objects`
145
- - reuse the same domain objects across your application code and remote resource management
146
- - leverage TypeScript's type safety, IDE autocomplete, and refactoring tools
147
- - compose and test resource definitions like any other code
163
+ instead of HCL, YAML, or another DSL:
164
+ - write declarative instructions via TypeScript with [`domain-objects`](https://github.com/ehmpathy/domain-objects)
165
+ - reuse the domain objects and domain operations across your application code and declarative instructions
166
+ - leverage TypeScript's type safety, IDE autocomplete, and refactor tools
167
+ - compose and test declarative instructions like any other code
148
168
 
149
169
  ### enforced best practices
150
170
 
@@ -153,14 +173,14 @@ declastruct providers follow a pit-of-success pattern that guarantees:
153
173
  **idempotency** — all operations can be safely retried
154
174
  - `finsert`: find-or-insert (safe create)
155
175
  - `upsert`: update-or-insert (safe update)
156
- - running the same plan multiple times produces the same result
176
+ - repeat any operation multiple times, get the same result each time
157
177
 
158
178
  **explicit unique keys** — every resource declares how to uniquely identify it
159
179
  - prevents accidental duplicates
160
180
  - enables accurate comparison against live state
161
181
  - makes resource relationships clear, typesafe, and composable
162
182
 
163
- **observable change plans** — see exactly what will change before applying
183
+ **observable change plans** — see exactly what must change before you apply
164
184
  - diff view shows before & after for each resource
165
185
  - change actions: CREATE, UPDATE, KEEP, DESTROY
166
186
  - no surprises in production
@@ -175,14 +195,14 @@ declastruct is designed to support any resource construct through adapters:
175
195
  - `declastruct-github` — Github resources (repos, branches, protection, etc.)
176
196
  - etc
177
197
 
178
- **build your own provider** for any resource construct (GitHub repos, Slack channels, database records, etc.) by implementing the `DeclastructDao` and `DeclastructProvider` interfaces.
198
+ **build your own provider** for any resource construct (GitHub repos, Slack channels, database records, etc.) via the `DeclastructDao` and `DeclastructProvider` interfaces.
179
199
 
180
200
  ## use cases
181
201
 
182
- - **infrastructure as code** — manage AWS, GCP, Azure resources declaratively
183
- - **SaaS platform management** — manage Stripe customers, GitHub repos, Slack channels, etc declaratively
184
- - **database state management** — control database resource states declaratively
185
- - **api state management** — control remote resource state through api's declaratively
202
+ - **infrastructure as code** — control AWS, GCP, Azure resources via declarative instructions
203
+ - **SaaS platform management** — control Stripe customers, GitHub repos, Slack channels via declarative instructions
204
+ - **database state management** — control database resource states via declarative instructions
205
+ - **api state management** — control remote resource state via declarative instructions
186
206
  - **multi-platform orchestration** — coordinate resources across different providers in one plan
187
207
 
188
208
 
@@ -192,7 +212,7 @@ declastruct is designed to support any resource construct through adapters:
192
212
 
193
213
  ### DeclastructDao
194
214
 
195
- the core abstraction that defines how to interact with a resource type:
215
+ the core abstraction that defines how to get and set a resource type:
196
216
 
197
217
  ```ts
198
218
  interface DeclastructDao<TResource, TResourceClass, TContext> {
@@ -209,7 +229,7 @@ interface DeclastructDao<TResource, TResourceClass, TContext> {
209
229
  }
210
230
  ```
211
231
 
212
- every resource class gets a DeclastructDao that enforces safe, idempotent operations.
232
+ every resource class has a DeclastructDao that enforces safe, idempotent operations.
213
233
 
214
234
  ### DeclastructProvider
215
235
 
@@ -229,7 +249,7 @@ interface DeclastructProvider<TDaos, TContext> {
229
249
 
230
250
  ### DeclastructPlan
231
251
 
232
- captures the changes needed to achieve desired state:
252
+ captures the changes needed to reconcile reality with declared desires:
233
253
 
234
254
  ```ts
235
255
  interface DeclastructPlan {
@@ -249,4 +269,4 @@ each `DeclastructChange` includes:
249
269
 
250
270
  ## inspiration
251
271
 
252
- inspired by Terraform's declarative approach, but designed to eliminate state management overhead, work with any resource construct, and leverage TypeScript's type system for safer declarative resource management.
272
+ inspired by Terraform's declarative approach, but designed to eliminate state file overhead, work with any resource construct, be reusable across both prod codepaths and cicd control, and leverage TypeScript's type system for safer declarative instructions.
@@ -1,32 +0,0 @@
1
- import { DomainEntity } from 'domain-objects';
2
- import { DeclastructDao } from '../../../domain.objects/DeclastructDao';
3
- import { DeclastructProvider } from '../../../domain.objects/DeclastructProvider';
4
- /**
5
- * .what = demo resource for integration testing
6
- */
7
- interface DemoResource {
8
- exid: string;
9
- name: string;
10
- }
11
- declare class DemoResource extends DomainEntity<DemoResource> implements DemoResource {
12
- static unique: readonly ["exid"];
13
- }
14
- /**
15
- * .what = demo provider with on-disk persistence
16
- * .why = enables full integration testing with real CRUD operations
17
- */
18
- export declare const demoProvider: DeclastructProvider<{
19
- DemoResource: DeclastructDao<DemoResource, typeof DemoResource, any>;
20
- }, {}>;
21
- /**
22
- * .what = generates a sample DemoResource with automatic unique ID
23
- * .why = simplifies test resource creation without managing IDs manually
24
- * .note = use resource.clone({ name: 'New Name' }) to update properties while keeping same exid
25
- */
26
- export declare const genSampleDemoResource: (input: {
27
- name: string;
28
- }) => import("domain-objects/dist/manipulation/immute/withImmute").WithImmute<DemoResource>;
29
- /**
30
- * .what = export DemoResource class for test usage
31
- */
32
- export { DemoResource };