declastruct 1.5.2 → 1.6.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/contract/sdk/index.d.ts +2 -0
- package/dist/contract/sdk/index.js +4 -1
- package/dist/contract/sdk/index.js.map +1 -1
- package/dist/domain.objects/DeclastructDao.d.ts +2 -2
- package/dist/domain.objects/genDeclastructDao.d.ts +71 -0
- package/dist/domain.objects/genDeclastructDao.js +54 -0
- package/dist/domain.objects/genDeclastructDao.js.map +1 -0
- package/dist/domain.operations/ref/getRefByPrimary.d.ts +1 -1
- package/dist/domain.operations/ref/getRefByPrimary.js +2 -4
- package/dist/domain.operations/ref/getRefByPrimary.js.map +1 -1
- package/dist/domain.operations/ref/getRefByUnique.d.ts +1 -1
- package/dist/domain.operations/ref/getRefByUnique.js +2 -4
- package/dist/domain.operations/ref/getRefByUnique.js.map +1 -1
- package/package.json +1 -1
- package/readme.md +142 -0
|
@@ -4,6 +4,8 @@ export { DeclastructChange, DeclastructChangeAction, } from '../../domain.object
|
|
|
4
4
|
export { DeclastructDao } from '../../domain.objects/DeclastructDao';
|
|
5
5
|
export { DeclastructPlan } from '../../domain.objects/DeclastructPlan';
|
|
6
6
|
export { DeclastructProvider } from '../../domain.objects/DeclastructProvider';
|
|
7
|
+
export type { DeclastructDaoInput, DeclastructDaoWithRef, DeclastructDaoWoutRef, } from '../../domain.objects/genDeclastructDao';
|
|
8
|
+
export { genDeclastructDao } from '../../domain.objects/genDeclastructDao';
|
|
7
9
|
export type { IsoTimestamp } from '../../domain.objects/IsoTimestamp';
|
|
8
10
|
export { getRefByPrimary } from '../../domain.operations/ref/getRefByPrimary';
|
|
9
11
|
export { getRefByUnique } from '../../domain.operations/ref/getRefByUnique';
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
// domain objects
|
|
3
3
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
-
exports.getRefByUnique = exports.getRefByPrimary = exports.DeclastructProvider = exports.DeclastructPlan = exports.DeclastructDao = exports.DeclastructChangeAction = exports.DeclastructChange = void 0;
|
|
4
|
+
exports.getRefByUnique = exports.getRefByPrimary = exports.genDeclastructDao = exports.DeclastructProvider = exports.DeclastructPlan = exports.DeclastructDao = exports.DeclastructChangeAction = exports.DeclastructChange = void 0;
|
|
5
5
|
var DeclastructChange_1 = require("../../domain.objects/DeclastructChange");
|
|
6
6
|
Object.defineProperty(exports, "DeclastructChange", { enumerable: true, get: function () { return DeclastructChange_1.DeclastructChange; } });
|
|
7
7
|
Object.defineProperty(exports, "DeclastructChangeAction", { enumerable: true, get: function () { return DeclastructChange_1.DeclastructChangeAction; } });
|
|
@@ -11,6 +11,9 @@ var DeclastructPlan_1 = require("../../domain.objects/DeclastructPlan");
|
|
|
11
11
|
Object.defineProperty(exports, "DeclastructPlan", { enumerable: true, get: function () { return DeclastructPlan_1.DeclastructPlan; } });
|
|
12
12
|
var DeclastructProvider_1 = require("../../domain.objects/DeclastructProvider");
|
|
13
13
|
Object.defineProperty(exports, "DeclastructProvider", { enumerable: true, get: function () { return DeclastructProvider_1.DeclastructProvider; } });
|
|
14
|
+
// factories
|
|
15
|
+
var genDeclastructDao_1 = require("../../domain.objects/genDeclastructDao");
|
|
16
|
+
Object.defineProperty(exports, "genDeclastructDao", { enumerable: true, get: function () { return genDeclastructDao_1.genDeclastructDao; } });
|
|
14
17
|
// domain operations
|
|
15
18
|
var getRefByPrimary_1 = require("../../domain.operations/ref/getRefByPrimary");
|
|
16
19
|
Object.defineProperty(exports, "getRefByPrimary", { enumerable: true, get: function () { return getRefByPrimary_1.getRefByPrimary; } });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/contract/sdk/index.ts"],"names":[],"mappings":";AAAA,iBAAiB;;;AAIjB,4EAGgD;AAF9C,sHAAA,iBAAiB,OAAA;AACjB,4HAAA,uBAAuB,OAAA;AAEzB,sEAAqE;AAA5D,gHAAA,cAAc,OAAA;AACvB,wEAAuE;AAA9D,kHAAA,eAAe,OAAA;AACxB,gFAA+E;AAAtE,0HAAA,mBAAmB,OAAA;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/contract/sdk/index.ts"],"names":[],"mappings":";AAAA,iBAAiB;;;AAIjB,4EAGgD;AAF9C,sHAAA,iBAAiB,OAAA;AACjB,4HAAA,uBAAuB,OAAA;AAEzB,sEAAqE;AAA5D,gHAAA,cAAc,OAAA;AACvB,wEAAuE;AAA9D,kHAAA,eAAe,OAAA;AACxB,gFAA+E;AAAtE,0HAAA,mBAAmB,OAAA;AAM5B,YAAY;AACZ,4EAA2E;AAAlE,sHAAA,iBAAiB,OAAA;AAE1B,oBAAoB;AACpB,+EAA8E;AAArE,kHAAA,eAAe,OAAA;AACxB,6EAA4E;AAAnE,gHAAA,cAAc,OAAA"}
|
|
@@ -56,14 +56,14 @@ export interface DeclastructDao<TResourceClass extends Refable<any, any, any>, T
|
|
|
56
56
|
*
|
|
57
57
|
* .note = set to null if resource lacks primary keys
|
|
58
58
|
*/
|
|
59
|
-
byPrimary: null | ((input: Ref<TResourceClass>, context: TContext) => Promise<RefByPrimary<TResourceClass
|
|
59
|
+
byPrimary: null | ((input: Ref<TResourceClass>, context: TContext) => Promise<RefByPrimary<TResourceClass> | null>);
|
|
60
60
|
/**
|
|
61
61
|
* .what = resolve any ref to RefByUnique
|
|
62
62
|
* .why = enables getting unique key from any ref type
|
|
63
63
|
*
|
|
64
64
|
* .note = set to null if resource lacks primary keys
|
|
65
65
|
*/
|
|
66
|
-
byUnique: null | ((input: Ref<TResourceClass>, context: TContext) => Promise<RefByUnique<TResourceClass
|
|
66
|
+
byUnique: null | ((input: Ref<TResourceClass>, context: TContext) => Promise<RefByUnique<TResourceClass> | null>);
|
|
67
67
|
};
|
|
68
68
|
};
|
|
69
69
|
/**
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { type Ref, type Refable, type RefByPrimary, type RefByUnique } from 'domain-objects';
|
|
2
|
+
import type { DeclastructDao } from './DeclastructDao';
|
|
3
|
+
/**
|
|
4
|
+
* .what = input type for genDeclastructDao without get.ref and get.one.byRef (auto-wired by factory)
|
|
5
|
+
* .why = derives from DeclastructDao to stay in sync, omits auto-wired methods
|
|
6
|
+
*/
|
|
7
|
+
export type DeclastructDaoInput<TResourceClass extends Refable<any, any, any>, TContext> = Omit<DeclastructDao<TResourceClass, TContext>, 'get'> & {
|
|
8
|
+
get: {
|
|
9
|
+
one: Omit<DeclastructDao<TResourceClass, TContext>['get']['one'], 'byRef'>;
|
|
10
|
+
};
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* .what = input type for daos with byPrimary defined
|
|
14
|
+
* .why = enables overload to distinguish daos with primary key support
|
|
15
|
+
*/
|
|
16
|
+
export type DeclastructDaoInputWithPrimary<TResourceClass extends Refable<any, any, any>, TContext> = Omit<DeclastructDaoInput<TResourceClass, TContext>, 'get'> & {
|
|
17
|
+
get: {
|
|
18
|
+
one: Omit<DeclastructDao<TResourceClass, TContext>['get']['one'], 'byPrimary' | 'byRef'> & {
|
|
19
|
+
byPrimary: NonNullable<DeclastructDao<TResourceClass, TContext>['get']['one']['byPrimary']>;
|
|
20
|
+
};
|
|
21
|
+
};
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* .what = input type for daos without byPrimary
|
|
25
|
+
* .why = enables overload to distinguish daos without primary key support
|
|
26
|
+
*/
|
|
27
|
+
export type DeclastructDaoInputWoutPrimary<TResourceClass extends Refable<any, any, any>, TContext> = Omit<DeclastructDaoInput<TResourceClass, TContext>, 'get'> & {
|
|
28
|
+
get: {
|
|
29
|
+
one: Omit<DeclastructDao<TResourceClass, TContext>['get']['one'], 'byPrimary' | 'byRef'> & {
|
|
30
|
+
byPrimary: null;
|
|
31
|
+
};
|
|
32
|
+
};
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* .what = output type for daos with ref methods defined
|
|
36
|
+
* .why = enables type-safe access to ref resolution methods
|
|
37
|
+
*/
|
|
38
|
+
export type DeclastructDaoWithRef<TResourceClass extends Refable<any, any, any>, TContext> = Omit<DeclastructDao<TResourceClass, TContext>, 'get'> & {
|
|
39
|
+
get: {
|
|
40
|
+
one: DeclastructDao<TResourceClass, TContext>['get']['one'];
|
|
41
|
+
ref: {
|
|
42
|
+
byPrimary: (input: Ref<TResourceClass>, context: TContext) => Promise<RefByPrimary<TResourceClass> | null>;
|
|
43
|
+
byUnique: (input: Ref<TResourceClass>, context: TContext) => Promise<RefByUnique<TResourceClass> | null>;
|
|
44
|
+
};
|
|
45
|
+
};
|
|
46
|
+
};
|
|
47
|
+
/**
|
|
48
|
+
* .what = output type for daos without ref methods
|
|
49
|
+
* .why = forces explicit null for ref methods when byPrimary is not supported
|
|
50
|
+
*/
|
|
51
|
+
export type DeclastructDaoWoutRef<TResourceClass extends Refable<any, any, any>, TContext> = Omit<DeclastructDao<TResourceClass, TContext>, 'get'> & {
|
|
52
|
+
get: {
|
|
53
|
+
one: DeclastructDao<TResourceClass, TContext>['get']['one'];
|
|
54
|
+
ref: {
|
|
55
|
+
byPrimary: null;
|
|
56
|
+
byUnique: null;
|
|
57
|
+
};
|
|
58
|
+
};
|
|
59
|
+
};
|
|
60
|
+
/**
|
|
61
|
+
* .what = factory to create DeclastructDao with auto-wired ref methods
|
|
62
|
+
* .why = enforces that ref methods are defined iff byPrimary is defined
|
|
63
|
+
* .note = TContext must be explicitly provided as a generic parameter to avoid hazardous Record<string, any> inference
|
|
64
|
+
*
|
|
65
|
+
* @overload input with byPrimary defined → output with ref methods defined
|
|
66
|
+
*/
|
|
67
|
+
export declare function genDeclastructDao<TResourceClass extends Refable<any, any, any>, TContext extends Record<string, any> = never>(input: DeclastructDaoInputWithPrimary<TResourceClass, TContext>): DeclastructDaoWithRef<TResourceClass, TContext>;
|
|
68
|
+
/**
|
|
69
|
+
* @overload input without byPrimary → output with ref methods null
|
|
70
|
+
*/
|
|
71
|
+
export declare function genDeclastructDao<TResourceClass extends Refable<any, any, any>, TContext extends Record<string, any> = never>(input: DeclastructDaoInputWoutPrimary<TResourceClass, TContext>): DeclastructDaoWoutRef<TResourceClass, TContext>;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.genDeclastructDao = void 0;
|
|
4
|
+
const domain_objects_1 = require("domain-objects");
|
|
5
|
+
const helpful_errors_1 = require("helpful-errors");
|
|
6
|
+
const getRefByPrimary_1 = require("../domain.operations/ref/getRefByPrimary");
|
|
7
|
+
const getRefByUnique_1 = require("../domain.operations/ref/getRefByUnique");
|
|
8
|
+
/**
|
|
9
|
+
* .impl = creates dao with byRef and ref methods auto-wired based on byPrimary presence
|
|
10
|
+
*/
|
|
11
|
+
function genDeclastructDao(input) {
|
|
12
|
+
const hasPrimary = input.get.one.byPrimary !== null;
|
|
13
|
+
// build byRef from byUnique + byPrimary
|
|
14
|
+
const byRef = async (ref, context) => {
|
|
15
|
+
if ((0, domain_objects_1.isRefByPrimary)({ of: input.dobj })(ref) && input.get.one.byPrimary)
|
|
16
|
+
return input.get.one.byPrimary(ref, context);
|
|
17
|
+
if ((0, domain_objects_1.isRefByUnique)({ of: input.dobj })(ref))
|
|
18
|
+
return input.get.one.byUnique(ref, context);
|
|
19
|
+
throw new helpful_errors_1.BadRequestError('get.one.byRef called with neither a RefByUnique nor RefByPrimary', { ref });
|
|
20
|
+
};
|
|
21
|
+
if (hasPrimary) {
|
|
22
|
+
// auto-wire ref methods using the helper functions
|
|
23
|
+
const dao = {
|
|
24
|
+
...input,
|
|
25
|
+
get: {
|
|
26
|
+
one: {
|
|
27
|
+
...input.get.one,
|
|
28
|
+
byRef,
|
|
29
|
+
},
|
|
30
|
+
ref: {
|
|
31
|
+
byPrimary: (ref, context) => (0, getRefByPrimary_1.getRefByPrimary)({ ref }, { dao, ...context }),
|
|
32
|
+
byUnique: (ref, context) => (0, getRefByUnique_1.getRefByUnique)({ ref }, { dao, ...context }),
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
return dao;
|
|
37
|
+
}
|
|
38
|
+
// no primary key support - ref methods are null
|
|
39
|
+
return {
|
|
40
|
+
...input,
|
|
41
|
+
get: {
|
|
42
|
+
one: {
|
|
43
|
+
...input.get.one,
|
|
44
|
+
byRef,
|
|
45
|
+
},
|
|
46
|
+
ref: {
|
|
47
|
+
byPrimary: null,
|
|
48
|
+
byUnique: null,
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
exports.genDeclastructDao = genDeclastructDao;
|
|
54
|
+
//# sourceMappingURL=genDeclastructDao.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"genDeclastructDao.js","sourceRoot":"","sources":["../../src/domain.objects/genDeclastructDao.ts"],"names":[],"mappings":";;;AAAA,mDAOwB;AACxB,mDAAiD;AAEjD,8EAA2E;AAC3E,4EAAyE;AAwHzE;;GAEG;AACH,SAAgB,iBAAiB,CAI/B,KAAoD;IAEpD,MAAM,UAAU,GAAG,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,SAAS,KAAK,IAAI,CAAC;IAEpD,wCAAwC;IACxC,MAAM,KAAK,GAAG,KAAK,EACjB,GAAwB,EACxB,OAAiB,EAC6B,EAAE;QAChD,IAAI,IAAA,+BAAc,EAAC,EAAE,EAAE,EAAE,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,SAAS;YACpE,OAAO,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;QAC/C,IAAI,IAAA,8BAAa,EAAC,EAAE,EAAE,EAAE,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC;YACxC,OAAO,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,QAAQ,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;QAC9C,MAAM,IAAI,gCAAe,CACvB,kEAAkE,EAClE,EAAE,GAAG,EAAE,CACR,CAAC;IACJ,CAAC,CAAC;IAEF,IAAI,UAAU,EAAE,CAAC;QACf,mDAAmD;QACnD,MAAM,GAAG,GAA6C;YACpD,GAAG,KAAK;YACR,GAAG,EAAE;gBACH,GAAG,EAAE;oBACH,GAAG,KAAK,CAAC,GAAG,CAAC,GAAG;oBAChB,KAAK;iBACN;gBACD,GAAG,EAAE;oBACH,SAAS,EAAE,CAAC,GAAG,EAAE,OAAO,EAAE,EAAE,CAC1B,IAAA,iCAAe,EAAC,EAAE,GAAG,EAAE,EAAE,EAAE,GAAG,EAAE,GAAG,OAAO,EAAE,CAAC;oBAC/C,QAAQ,EAAE,CAAC,GAAG,EAAE,OAAO,EAAE,EAAE,CACzB,IAAA,+BAAc,EAAC,EAAE,GAAG,EAAE,EAAE,EAAE,GAAG,EAAE,GAAG,OAAO,EAAE,CAAC;iBAC/C;aACF;SACF,CAAC;QACF,OAAO,GAAG,CAAC;IACb,CAAC;IAED,gDAAgD;IAChD,OAAO;QACL,GAAG,KAAK;QACR,GAAG,EAAE;YACH,GAAG,EAAE;gBACH,GAAG,KAAK,CAAC,GAAG,CAAC,GAAG;gBAChB,KAAK;aACN;YACD,GAAG,EAAE;gBACH,SAAS,EAAE,IAAI;gBACf,QAAQ,EAAE,IAAI;aACf;SACF;KACF,CAAC;AACJ,CAAC;AAzDD,8CAyDC"}
|
|
@@ -9,4 +9,4 @@ export declare const getRefByPrimary: <TResourceClass extends Refable<any, any,
|
|
|
9
9
|
ref: Ref<TResourceClass>;
|
|
10
10
|
}, context: {
|
|
11
11
|
dao: DeclastructDao<TResourceClass, TContext>;
|
|
12
|
-
} & TContext) => Promise<RefByPrimary<TResourceClass
|
|
12
|
+
} & TContext) => Promise<RefByPrimary<TResourceClass> | null>;
|
|
@@ -16,11 +16,9 @@ const getRefByPrimary = async (input, context) => {
|
|
|
16
16
|
if ((0, domain_objects_1.isRefByUnique)({ of: context.dao.dobj })(input.ref)) {
|
|
17
17
|
// fetch the resource by unique key
|
|
18
18
|
const resource = await context.dao.get.one.byUnique(input.ref, context);
|
|
19
|
-
//
|
|
19
|
+
// return null if resource not found (resource may not exist yet)
|
|
20
20
|
if (!resource)
|
|
21
|
-
|
|
22
|
-
ref: input.ref,
|
|
23
|
-
});
|
|
21
|
+
return null;
|
|
24
22
|
// extract primary key from the resource
|
|
25
23
|
return (0, domain_objects_1.refByPrimary)(resource);
|
|
26
24
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"getRefByPrimary.js","sourceRoot":"","sources":["../../../src/domain.operations/ref/getRefByPrimary.ts"],"names":[],"mappings":";;;AAAA,mDAOwB;AACxB,
|
|
1
|
+
{"version":3,"file":"getRefByPrimary.js","sourceRoot":"","sources":["../../../src/domain.operations/ref/getRefByPrimary.ts"],"names":[],"mappings":";;;AAAA,mDAOwB;AACxB,mDAAyD;AAIzD;;;;GAIG;AACI,MAAM,eAAe,GAAG,KAAK,EAIlC,KAEC,EACD,OAEY,EACkC,EAAE;IAChD,wDAAwD;IACxD,IAAI,IAAA,+BAAc,EAAC,EAAE,EAAE,EAAE,OAAO,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC;QAAE,OAAO,KAAK,CAAC,GAAG,CAAC;IAE1E,yDAAyD;IACzD,IAAI,IAAA,8BAAa,EAAC,EAAE,EAAE,EAAE,OAAO,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;QACvD,mCAAmC;QACnC,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;QAExE,iEAAiE;QACjE,IAAI,CAAC,QAAQ;YAAE,OAAO,IAAI,CAAC;QAE3B,wCAAwC;QACxC,OAAO,IAAA,6BAAY,EAAiB,QAAQ,CAAC,CAAC;IAChD,CAAC;IAED,iCAAiC;IACjC,MAAM,IAAI,wCAAuB,CAAC,kBAAkB,EAAE,EAAE,GAAG,EAAE,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC;AAC5E,CAAC,CAAC;AA5BW,QAAA,eAAe,mBA4B1B"}
|
|
@@ -9,4 +9,4 @@ export declare const getRefByUnique: <TResourceClass extends Refable<any, any, a
|
|
|
9
9
|
ref: Ref<TResourceClass>;
|
|
10
10
|
}, context: {
|
|
11
11
|
dao: DeclastructDao<TResourceClass, TContext>;
|
|
12
|
-
} & TContext) => Promise<RefByUnique<TResourceClass
|
|
12
|
+
} & TContext) => Promise<RefByUnique<TResourceClass> | null>;
|
|
@@ -19,11 +19,9 @@ const getRefByUnique = async (input, context) => {
|
|
|
19
19
|
throw new helpful_errors_1.UnexpectedCodePathError('dao does not support byPrimary lookup', { ref: input.ref });
|
|
20
20
|
// fetch the resource by primary key
|
|
21
21
|
const resource = await context.dao.get.one.byPrimary(input.ref, context);
|
|
22
|
-
//
|
|
22
|
+
// return null if resource not found (resource may not exist yet)
|
|
23
23
|
if (!resource)
|
|
24
|
-
|
|
25
|
-
ref: input.ref,
|
|
26
|
-
});
|
|
24
|
+
return null;
|
|
27
25
|
// extract unique key from the resource
|
|
28
26
|
return (0, domain_objects_1.refByUnique)(resource);
|
|
29
27
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"getRefByUnique.js","sourceRoot":"","sources":["../../../src/domain.operations/ref/getRefByUnique.ts"],"names":[],"mappings":";;;AAAA,mDAOwB;AACxB,
|
|
1
|
+
{"version":3,"file":"getRefByUnique.js","sourceRoot":"","sources":["../../../src/domain.operations/ref/getRefByUnique.ts"],"names":[],"mappings":";;;AAAA,mDAOwB;AACxB,mDAAyD;AAIzD;;;;GAIG;AACI,MAAM,cAAc,GAAG,KAAK,EAIjC,KAEC,EACD,OAEY,EACiC,EAAE;IAC/C,uDAAuD;IACvD,IAAI,IAAA,8BAAa,EAAC,EAAE,EAAE,EAAE,OAAO,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC;QAAE,OAAO,KAAK,CAAC,GAAG,CAAC;IAEzE,yDAAyD;IACzD,IAAI,IAAA,+BAAc,EAAC,EAAE,EAAE,EAAE,OAAO,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;QACxD,gCAAgC;QAChC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,SAAS;YAChC,MAAM,IAAI,wCAAuB,CAC/B,uCAAuC,EACvC,EAAE,GAAG,EAAE,KAAK,CAAC,GAAG,EAAE,CACnB,CAAC;QAEJ,oCAAoC;QACpC,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,SAAS,CAAC,KAAK,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;QAEzE,iEAAiE;QACjE,IAAI,CAAC,QAAQ;YAAE,OAAO,IAAI,CAAC;QAE3B,uCAAuC;QACvC,OAAO,IAAA,4BAAW,EAAiB,QAAQ,CAAC,CAAC;IAC/C,CAAC;IAED,iCAAiC;IACjC,MAAM,IAAI,wCAAuB,CAAC,kBAAkB,EAAE,EAAE,GAAG,EAAE,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC;AAC5E,CAAC,CAAC;AAnCW,QAAA,cAAc,kBAmCzB"}
|
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.
|
|
5
|
+
"version": "1.6.0",
|
|
6
6
|
"repository": "ehmpathy/declastruct",
|
|
7
7
|
"homepage": "https://github.com/ehmpathy/declastruct",
|
|
8
8
|
"keywords": [
|
package/readme.md
CHANGED
|
@@ -270,3 +270,145 @@ each `DeclastructChange` includes:
|
|
|
270
270
|
## inspiration
|
|
271
271
|
|
|
272
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.
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
# plugin
|
|
276
|
+
|
|
277
|
+
build your own provider to control any resource construct via declastruct.
|
|
278
|
+
|
|
279
|
+
## 1. declare your resource
|
|
280
|
+
|
|
281
|
+
define your resource as a [`domain-object`](https://github.com/ehmpathy/domain-objects) with key attributes:
|
|
282
|
+
|
|
283
|
+
```ts
|
|
284
|
+
import { DomainEntity } from 'domain-objects';
|
|
285
|
+
|
|
286
|
+
interface DeclaredStripeCustomer {
|
|
287
|
+
id?: string;
|
|
288
|
+
email: string;
|
|
289
|
+
name: string;
|
|
290
|
+
status?: string;
|
|
291
|
+
}
|
|
292
|
+
class DeclaredStripeCustomer
|
|
293
|
+
extends DomainEntity<DeclaredStripeCustomer>
|
|
294
|
+
implements DeclaredStripeCustomer
|
|
295
|
+
{
|
|
296
|
+
// primary key: the surrogate key identifier for this resource
|
|
297
|
+
public static primary = ['id'] as const;
|
|
298
|
+
|
|
299
|
+
// unique key: the natural key identifier for this resource (i.e., the non-primary unique key)
|
|
300
|
+
public static unique = ['email'] as const;
|
|
301
|
+
|
|
302
|
+
// metadata keys: readonly persistance-specific fields - excluded from change detection
|
|
303
|
+
// if unspecified, the defaults of 'id', 'uuid', 'createdAt', 'updatedAt', 'effectiveAt' will be assumed
|
|
304
|
+
public static metadata = ['id'] as const;
|
|
305
|
+
|
|
306
|
+
// readonly keys: readonly domain-intrinsic fields - excluded from change detection
|
|
307
|
+
// note: all metadata is implicitly readonly, so you only need to specify non-metadata readonly keys here
|
|
308
|
+
public static readonly = ['status'] as const;
|
|
309
|
+
}
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
key attributes to consider:
|
|
313
|
+
- **primary key** (`static primary`): surrogate identifier for efficient lookups after creation
|
|
314
|
+
- **unique key** (`static unique`): natural key that uniquely identifies the resource, used for idempotent operations
|
|
315
|
+
- **metadata keys** (`static metadata`): readonly persistence-layer fields that describe *how* the object is stored (e.g., `uuid`, `createdAt`). note, metadata is a specific subset of readonly
|
|
316
|
+
- **readonly keys** (`static readonly`): readonly domain-layer fields that describe *what* the object is (e.g., `status`). note, you only need to declare the readonly attributes that are not already flagged as metadata here
|
|
317
|
+
|
|
318
|
+
## 2. generate your dao
|
|
319
|
+
|
|
320
|
+
use `genDeclastructDao` to create a type-safe DAO with auto-wired ref resolution:
|
|
321
|
+
|
|
322
|
+
```ts
|
|
323
|
+
import { genDeclastructDao } from 'declastruct';
|
|
324
|
+
import Stripe from 'stripe';
|
|
325
|
+
|
|
326
|
+
// define the context type for your provider
|
|
327
|
+
interface StripeContext {
|
|
328
|
+
stripe: Stripe;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
const daoStripeCustomer = genDeclastructDao<
|
|
332
|
+
typeof DeclaredStripeCustomer,
|
|
333
|
+
StripeContext
|
|
334
|
+
>({
|
|
335
|
+
dobj: DeclaredStripeCustomer,
|
|
336
|
+
get: {
|
|
337
|
+
one: {
|
|
338
|
+
byUnique: async ({ email }, context) => {
|
|
339
|
+
// lookup by natural key (email)
|
|
340
|
+
const customer = await context.stripe.customers.list({ email });
|
|
341
|
+
return customer.data[0] ? toResource(customer.data[0]) : null;
|
|
342
|
+
},
|
|
343
|
+
byPrimary: async ({ id }, context) => {
|
|
344
|
+
// lookup by stripe-assigned id
|
|
345
|
+
const customer = await context.stripe.customers.retrieve(id);
|
|
346
|
+
return customer ? toResource(customer) : null;
|
|
347
|
+
},
|
|
348
|
+
},
|
|
349
|
+
},
|
|
350
|
+
set: {
|
|
351
|
+
finsert: async (resource, context) => {
|
|
352
|
+
// find-or-insert: idempotent create
|
|
353
|
+
const foundBefore = await daoStripeCustomer.get.one.byUnique(
|
|
354
|
+
{ email: resource.email },
|
|
355
|
+
context,
|
|
356
|
+
);
|
|
357
|
+
if (foundBefore) return foundBefore;
|
|
358
|
+
|
|
359
|
+
const created = await context.stripe.customers.create({
|
|
360
|
+
email: resource.email,
|
|
361
|
+
name: resource.name,
|
|
362
|
+
});
|
|
363
|
+
return toResource(created);
|
|
364
|
+
},
|
|
365
|
+
upsert: async (resource, context) => {
|
|
366
|
+
// update-or-insert: idempotent update
|
|
367
|
+
const foundBefore = await daoStripeCustomer.get.one.byUnique(
|
|
368
|
+
{ email: resource.email },
|
|
369
|
+
context,
|
|
370
|
+
);
|
|
371
|
+
if (foundBefore) {
|
|
372
|
+
const updated = await context.stripe.customers.update(foundBefore.id, {
|
|
373
|
+
name: resource.name,
|
|
374
|
+
});
|
|
375
|
+
return toResource(updated);
|
|
376
|
+
}
|
|
377
|
+
return daoStripeCustomer.set.finsert(resource, context);
|
|
378
|
+
},
|
|
379
|
+
delete: async (ref, context) => {
|
|
380
|
+
const foundBefore = await daoStripeCustomer.get.one.byRef(ref, context);
|
|
381
|
+
if (foundBefore) await context.stripe.customers.del(foundBefore.id);
|
|
382
|
+
},
|
|
383
|
+
},
|
|
384
|
+
});
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
the factory automatically:
|
|
388
|
+
- **builds get.one.byRef**: `get.one.byRef` is built for you, based on `get.one.byUnique` and `get.one.byPrimary`
|
|
389
|
+
- **builds get.ref.byPrimary,.byUnique**: if `byPrimary` is defined, `get.ref.byPrimary` and `get.ref.byUnique` are built for you, based on `get.one.byUnique` and `get.one.byPrimary`
|
|
390
|
+
- **enforces type safety**: if `byPrimary` is null, `get.ref.*` are typed as null (prevents accidental calls)
|
|
391
|
+
|
|
392
|
+
## 3. create your provider
|
|
393
|
+
|
|
394
|
+
bundle your DAOs into a provider:
|
|
395
|
+
|
|
396
|
+
```ts
|
|
397
|
+
import { DeclastructProvider } from 'declastruct';
|
|
398
|
+
|
|
399
|
+
export const stripeProvider = new DeclastructProvider({
|
|
400
|
+
name: 'stripe',
|
|
401
|
+
daos: {
|
|
402
|
+
DeclaredStripeCustomer: daoStripeCustomer,
|
|
403
|
+
},
|
|
404
|
+
context: {
|
|
405
|
+
stripe, // stripe client instance
|
|
406
|
+
},
|
|
407
|
+
hooks: {
|
|
408
|
+
beforeAll: async () => { /* setup */ },
|
|
409
|
+
afterAll: async () => { /* cleanup */ },
|
|
410
|
+
},
|
|
411
|
+
});
|
|
412
|
+
```
|
|
413
|
+
|
|
414
|
+
now users can declare stripe customers and use `plan` + `apply` to control them declaratively.
|