@moshaver/cmb-starter 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +73 -0
- package/package.json +58 -0
- package/src/index.d.ts +27 -0
- package/src/index.js +94 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Mobin Karam
|
|
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/README.md
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# @moshaver/cmb-starter
|
|
2
|
+
|
|
3
|
+
[English](#english) | [فارسی](#فارسی)
|
|
4
|
+
|
|
5
|
+
## English
|
|
6
|
+
|
|
7
|
+
`@moshaver/cmb-starter` creates a framework-neutral CMB runtime from one small
|
|
8
|
+
configuration object. It resolves module dependencies, exposes lifecycle and
|
|
9
|
+
readiness APIs, and leaves HTTP transport, database drivers, secrets, routes,
|
|
10
|
+
and product policy to the host application.
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
npm install @moshaver/cmb-starter
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Create one `cmb.config.cjs` file:
|
|
17
|
+
|
|
18
|
+
```js
|
|
19
|
+
module.exports = {
|
|
20
|
+
serviceName: "notes-api",
|
|
21
|
+
modules: ["auth", "authorization", "notifications", "realtime"],
|
|
22
|
+
};
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Load it at the application's composition root:
|
|
26
|
+
|
|
27
|
+
```js
|
|
28
|
+
const config = require("./cmb.config.cjs");
|
|
29
|
+
const { createCmbRuntime } = require("@moshaver/cmb-starter");
|
|
30
|
+
|
|
31
|
+
const cmb = createCmbRuntime(config);
|
|
32
|
+
await cmb.start();
|
|
33
|
+
await cmb.ready();
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Dependencies such as `identity` and `tenancy` are resolved automatically. Add
|
|
37
|
+
your own product module descriptors through `registrations`; keep product
|
|
38
|
+
routes, roles, storage adapters, and secrets outside this package.
|
|
39
|
+
|
|
40
|
+
## فارسی
|
|
41
|
+
|
|
42
|
+
پکیج `@moshaver/cmb-starter` با یک شیء پیکربندی کوچک، runtime مستقل از
|
|
43
|
+
فریمورک CMB را میسازد. وابستگی ماژولها، چرخهٔ اجرا و وضعیت آمادهبودن را
|
|
44
|
+
مدیریت میکند؛ اما HTTP، درایور پایگاهداده، رازها، مسیرها و قوانین محصول
|
|
45
|
+
همچنان مسئولیت برنامهٔ میزبان هستند.
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
npm install @moshaver/cmb-starter
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
یک فایل `cmb.config.cjs` بسازید:
|
|
52
|
+
|
|
53
|
+
```js
|
|
54
|
+
module.exports = {
|
|
55
|
+
serviceName: "notes-api",
|
|
56
|
+
modules: ["auth", "authorization", "notifications", "realtime"],
|
|
57
|
+
};
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
سپس آن را در composition root برنامه بارگذاری کنید:
|
|
61
|
+
|
|
62
|
+
```js
|
|
63
|
+
const config = require("./cmb.config.cjs");
|
|
64
|
+
const { createCmbRuntime } = require("@moshaver/cmb-starter");
|
|
65
|
+
|
|
66
|
+
const cmb = createCmbRuntime(config);
|
|
67
|
+
await cmb.start();
|
|
68
|
+
await cmb.ready();
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
وابستگیهایی مانند `identity` و `tenancy` خودکار حل میشوند. descriptorهای
|
|
72
|
+
محصول خود را با `registrations` اضافه کنید و مسیرها، نقشها، adapterهای ذخیرهسازی
|
|
73
|
+
و رازهای محصول را بیرون از این پکیج نگه دارید.
|
package/package.json
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@moshaver/cmb-starter",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Composable Modular Backend starter primitives.",
|
|
5
|
+
"type": "commonjs",
|
|
6
|
+
"main": "./src/index.js",
|
|
7
|
+
"types": "./src/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"require": "./src/index.js",
|
|
11
|
+
"types": "./src/index.d.ts"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"src",
|
|
16
|
+
"README.md",
|
|
17
|
+
"LICENSE"
|
|
18
|
+
],
|
|
19
|
+
"keywords": [
|
|
20
|
+
"cmb",
|
|
21
|
+
"modular-backend",
|
|
22
|
+
"backend",
|
|
23
|
+
"framework-agnostic",
|
|
24
|
+
"starter"
|
|
25
|
+
],
|
|
26
|
+
"engines": {
|
|
27
|
+
"node": ">=22.13.0 <23"
|
|
28
|
+
},
|
|
29
|
+
"repository": {
|
|
30
|
+
"type": "git",
|
|
31
|
+
"url": "git+https://github.com/Mobin-Karam/moshaver.git",
|
|
32
|
+
"directory": "packages/cmb/starter"
|
|
33
|
+
},
|
|
34
|
+
"bugs": {
|
|
35
|
+
"url": "https://github.com/Mobin-Karam/moshaver/issues"
|
|
36
|
+
},
|
|
37
|
+
"homepage": "https://github.com/Mobin-Karam/moshaver/tree/main/packages/cmb",
|
|
38
|
+
"publishConfig": {
|
|
39
|
+
"access": "public"
|
|
40
|
+
},
|
|
41
|
+
"license": "MIT",
|
|
42
|
+
"sideEffects": false,
|
|
43
|
+
"dependencies": {
|
|
44
|
+
"@moshaver/cmb-activity": "^0.1.0",
|
|
45
|
+
"@moshaver/cmb-auth": "^0.1.0",
|
|
46
|
+
"@moshaver/cmb-authorization": "^0.1.0",
|
|
47
|
+
"@moshaver/cmb-data-transfer": "^0.1.0",
|
|
48
|
+
"@moshaver/cmb-health": "^0.1.0",
|
|
49
|
+
"@moshaver/cmb-identity": "^0.1.0",
|
|
50
|
+
"@moshaver/cmb-infrastructure": "^0.1.0",
|
|
51
|
+
"@moshaver/cmb-kernel": "^0.1.0",
|
|
52
|
+
"@moshaver/cmb-notifications": "^0.1.0",
|
|
53
|
+
"@moshaver/cmb-persistence": "^0.1.0",
|
|
54
|
+
"@moshaver/cmb-realtime": "^0.1.0",
|
|
55
|
+
"@moshaver/cmb-system": "^0.1.0",
|
|
56
|
+
"@moshaver/cmb-tenancy": "^0.1.0"
|
|
57
|
+
}
|
|
58
|
+
}
|
package/src/index.d.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { CmbModuleDescriptor, CmbModuleRegistration, CmbModuleRegistry } from "@moshaver/cmb-kernel";
|
|
2
|
+
import type { CmbHealthService } from "@moshaver/cmb-health";
|
|
3
|
+
|
|
4
|
+
export const STARTER_MODULE: CmbModuleDescriptor;
|
|
5
|
+
export const builtInModules: Readonly<Record<string, CmbModuleDescriptor>>;
|
|
6
|
+
|
|
7
|
+
export type CmbStarterConfig = Readonly<{
|
|
8
|
+
serviceName?: string;
|
|
9
|
+
modules?: readonly (keyof typeof builtInModules)[];
|
|
10
|
+
registrations?: readonly CmbModuleDescriptor[] | readonly CmbModuleRegistration[];
|
|
11
|
+
}>;
|
|
12
|
+
|
|
13
|
+
export function normalizeConfig(input?: CmbStarterConfig): Readonly<{
|
|
14
|
+
serviceName: string;
|
|
15
|
+
modules: readonly string[];
|
|
16
|
+
registrations: readonly (CmbModuleDescriptor | CmbModuleRegistration)[];
|
|
17
|
+
}>;
|
|
18
|
+
|
|
19
|
+
export function createCmbRuntime(config?: CmbStarterConfig): Readonly<{
|
|
20
|
+
config: ReturnType<typeof normalizeConfig>;
|
|
21
|
+
registry: CmbModuleRegistry;
|
|
22
|
+
health: CmbHealthService;
|
|
23
|
+
metadata(): readonly CmbModuleDescriptor[];
|
|
24
|
+
ready(): ReturnType<CmbHealthService["ready"]>;
|
|
25
|
+
start(context?: Record<string, unknown>): ReturnType<CmbModuleRegistry["start"]>;
|
|
26
|
+
stop(context?: Record<string, unknown>): ReturnType<CmbModuleRegistry["stop"]>;
|
|
27
|
+
}>;
|
package/src/index.js
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const { CmbModuleRegistry, defineModule } = require("@moshaver/cmb-kernel");
|
|
4
|
+
const { HEALTH_MODULE, CmbHealthService } = require("@moshaver/cmb-health");
|
|
5
|
+
const { PERSISTENCE_MODULE } = require("@moshaver/cmb-persistence");
|
|
6
|
+
const { INFRASTRUCTURE_MODULE } = require("@moshaver/cmb-infrastructure");
|
|
7
|
+
const { REALTIME_MODULE } = require("@moshaver/cmb-realtime");
|
|
8
|
+
const { IDENTITY_MODULE } = require("@moshaver/cmb-identity");
|
|
9
|
+
const { TENANCY_MODULE } = require("@moshaver/cmb-tenancy");
|
|
10
|
+
const { SYSTEM_MODULE } = require("@moshaver/cmb-system");
|
|
11
|
+
const { AUTH_MODULE } = require("@moshaver/cmb-auth");
|
|
12
|
+
const { AUTHORIZATION_MODULE } = require("@moshaver/cmb-authorization");
|
|
13
|
+
const { NOTIFICATIONS_MODULE } = require("@moshaver/cmb-notifications");
|
|
14
|
+
const { ACTIVITY_MODULE } = require("@moshaver/cmb-activity");
|
|
15
|
+
const { DATA_TRANSFER_MODULE } = require("@moshaver/cmb-data-transfer");
|
|
16
|
+
|
|
17
|
+
const STARTER_MODULE = defineModule({
|
|
18
|
+
id: "starter",
|
|
19
|
+
version: "0.1.0",
|
|
20
|
+
kind: "platform",
|
|
21
|
+
dependencies: [
|
|
22
|
+
"activity", "auth", "authorization", "data-transfer", "health", "identity",
|
|
23
|
+
"infrastructure", "kernel", "notifications", "persistence", "realtime", "system", "tenancy",
|
|
24
|
+
],
|
|
25
|
+
provides: ["cmb.runtime"],
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
const builtInModules = Object.freeze({
|
|
29
|
+
health: HEALTH_MODULE,
|
|
30
|
+
persistence: PERSISTENCE_MODULE,
|
|
31
|
+
infrastructure: INFRASTRUCTURE_MODULE,
|
|
32
|
+
realtime: REALTIME_MODULE,
|
|
33
|
+
identity: IDENTITY_MODULE,
|
|
34
|
+
tenancy: TENANCY_MODULE,
|
|
35
|
+
system: SYSTEM_MODULE,
|
|
36
|
+
auth: AUTH_MODULE,
|
|
37
|
+
authorization: AUTHORIZATION_MODULE,
|
|
38
|
+
notifications: NOTIFICATIONS_MODULE,
|
|
39
|
+
activity: ACTIVITY_MODULE,
|
|
40
|
+
"data-transfer": DATA_TRANSFER_MODULE,
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
function normalizeConfig(input = {}) {
|
|
44
|
+
if (!input || typeof input !== "object" || Array.isArray(input)) {
|
|
45
|
+
throw new TypeError("CMB configuration must be an object.");
|
|
46
|
+
}
|
|
47
|
+
const serviceName = String(input.serviceName ?? "cmb-service").trim();
|
|
48
|
+
if (!serviceName) throw new TypeError("config.serviceName must not be empty.");
|
|
49
|
+
const registrations = input.registrations ?? [];
|
|
50
|
+
if (!Array.isArray(registrations)) throw new TypeError("config.registrations must be an array.");
|
|
51
|
+
const registeredIds = new Set(registrations.map((entry) => entry?.descriptor?.id ?? entry?.id));
|
|
52
|
+
const modules = input.modules ?? ["health"];
|
|
53
|
+
if (!Array.isArray(modules) || modules.some((id) => typeof id !== "string" || (!builtInModules[id] && !registeredIds.has(id)))) {
|
|
54
|
+
throw new TypeError("config.modules must contain built-in or registered CMB module ids.");
|
|
55
|
+
}
|
|
56
|
+
return Object.freeze({ serviceName, modules: [...new Set(["health", ...modules])], registrations });
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function createCmbRuntime(config) {
|
|
60
|
+
const normalized = normalizeConfig(config);
|
|
61
|
+
const registrations = [...Object.values(builtInModules), ...normalized.registrations];
|
|
62
|
+
const registry = new CmbModuleRegistry(registrations);
|
|
63
|
+
const descriptors = new Map(
|
|
64
|
+
registrations.map((entry) => {
|
|
65
|
+
const descriptor = entry?.descriptor ?? entry;
|
|
66
|
+
return [descriptor.id, descriptor];
|
|
67
|
+
}),
|
|
68
|
+
);
|
|
69
|
+
const enabled = new Set(["kernel"]);
|
|
70
|
+
const include = (id) => {
|
|
71
|
+
if (enabled.has(id)) return;
|
|
72
|
+
const descriptor = descriptors.get(id);
|
|
73
|
+
if (!descriptor) throw new Error(`CMB module ${id} is not registered.`);
|
|
74
|
+
enabled.add(id);
|
|
75
|
+
for (const dependency of descriptor.dependencies ?? []) include(dependency);
|
|
76
|
+
};
|
|
77
|
+
for (const id of normalized.modules) include(id);
|
|
78
|
+
const enabledModules = [...enabled];
|
|
79
|
+
const health = new CmbHealthService({
|
|
80
|
+
serviceName: normalized.serviceName,
|
|
81
|
+
probes: [{ name: "modules", check: () => registry.resolve(enabledModules) }],
|
|
82
|
+
});
|
|
83
|
+
return Object.freeze({
|
|
84
|
+
config: normalized,
|
|
85
|
+
registry,
|
|
86
|
+
health,
|
|
87
|
+
metadata: () => registry.metadata(enabledModules),
|
|
88
|
+
ready: () => health.ready(),
|
|
89
|
+
start: async (context = {}) => registry.start({ ...context, health }, enabledModules),
|
|
90
|
+
stop: async (context = {}) => registry.stop({ ...context, health }),
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
module.exports = { STARTER_MODULE, builtInModules, normalizeConfig, createCmbRuntime };
|