@node-in-layers/core-knowledge-mcp 1.0.0 → 1.10.10

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 (49) hide show
  1. package/README.md +29 -3
  2. package/bin/mcp_server.js +4 -4
  3. package/config.prod.mjs +1 -5
  4. package/package.json +3 -3
  5. package/bin/build.sh +0 -30
  6. package/dist/README.md +0 -7
  7. package/dist/bin/mcp_server.js +0 -29
  8. package/dist/bin/shell.mts +0 -77
  9. package/dist/config.prod.mjs +0 -61
  10. package/dist/package.json +0 -61
  11. package/src/knowledge/entries.json +0 -215
  12. package/src/knowledge/features.ts +0 -47
  13. package/src/knowledge/index.ts +0 -6
  14. package/src/knowledge/mcp.ts +0 -11
  15. package/src/knowledge/services.ts +0 -8
  16. package/src/knowledge/types.ts +0 -43
  17. package/src/mcp/index.ts +0 -4
  18. package/src/mcp/mcp.ts +0 -56
  19. package/src/mcp/types.ts +0 -25
  20. package/src/types.ts +0 -9
  21. package/tsconfig.json +0 -27
  22. /package/{dist/knowledge → knowledge}/entries.json +0 -0
  23. /package/{dist/knowledge → knowledge}/features.d.ts +0 -0
  24. /package/{dist/knowledge → knowledge}/features.js +0 -0
  25. /package/{dist/knowledge → knowledge}/features.js.map +0 -0
  26. /package/{dist/knowledge → knowledge}/index.d.ts +0 -0
  27. /package/{dist/knowledge → knowledge}/index.js +0 -0
  28. /package/{dist/knowledge → knowledge}/index.js.map +0 -0
  29. /package/{dist/knowledge → knowledge}/mcp.d.ts +0 -0
  30. /package/{dist/knowledge → knowledge}/mcp.js +0 -0
  31. /package/{dist/knowledge → knowledge}/mcp.js.map +0 -0
  32. /package/{dist/knowledge → knowledge}/services.d.ts +0 -0
  33. /package/{dist/knowledge → knowledge}/services.js +0 -0
  34. /package/{dist/knowledge → knowledge}/services.js.map +0 -0
  35. /package/{dist/knowledge → knowledge}/types.d.ts +0 -0
  36. /package/{dist/knowledge → knowledge}/types.js +0 -0
  37. /package/{dist/knowledge → knowledge}/types.js.map +0 -0
  38. /package/{dist/mcp → mcp}/index.d.ts +0 -0
  39. /package/{dist/mcp → mcp}/index.js +0 -0
  40. /package/{dist/mcp → mcp}/index.js.map +0 -0
  41. /package/{dist/mcp → mcp}/mcp.d.ts +0 -0
  42. /package/{dist/mcp → mcp}/mcp.js +0 -0
  43. /package/{dist/mcp → mcp}/mcp.js.map +0 -0
  44. /package/{dist/mcp → mcp}/types.d.ts +0 -0
  45. /package/{dist/mcp → mcp}/types.js +0 -0
  46. /package/{dist/mcp → mcp}/types.js.map +0 -0
  47. /package/{dist/types.d.ts → types.d.ts} +0 -0
  48. /package/{dist/types.js → types.js} +0 -0
  49. /package/{dist/types.js.map → types.js.map} +0 -0
package/README.md CHANGED
@@ -1,7 +1,33 @@
1
1
  # Development Mcp Tool for Node in Layers Core
2
+
2
3
  Whenever you build a system using node in layers core, you can use this MCP tool to teach the AI how to actually interact and work with your node in layers system.
3
4
 
4
5
  ## How To Use
5
- ```bash
6
- npx install @node-in-layers/core-knowledge-mcp
7
- ```
6
+
7
+ You can use this as a CLI MCP tool with any normal AI service. Here is an example used with Cursor.
8
+
9
+ ```json
10
+ {
11
+ "mcpServers": {
12
+ "node-in-layers-core": {
13
+ "command": "npx",
14
+ "args": ["-y", "@node-in-layers/core-knowledge-mcp"]
15
+ }
16
+ }
17
+ }
18
+ ```
19
+
20
+ ### Selecting a specific version.
21
+
22
+ If you want the development knowledge to match the version of Node In Layers Core you are using, you can put it in the npx command.
23
+
24
+ ```json
25
+ {
26
+ "mcpServers": {
27
+ "node-in-layers-core": {
28
+ "command": "npx",
29
+ "args": ["-y", "@node-in-layers/core-knowledge-mcp@1.10.10"]
30
+ }
31
+ }
32
+ }
33
+ ```
package/bin/mcp_server.js CHANGED
@@ -3,7 +3,7 @@
3
3
  import esMain from 'es-main'
4
4
  import { ArgumentParser } from 'argparse'
5
5
  import * as core from '@node-in-layers/core'
6
- import * as config from '../dist/config.prod.mjs'
6
+ import * as config from '../config.prod.mjs'
7
7
 
8
8
  const _parseArguments = () => {
9
9
  const parser = new ArgumentParser({
@@ -13,16 +13,16 @@ const _parseArguments = () => {
13
13
  }
14
14
 
15
15
  const startServer = async () => {
16
- const context = (await core.loadSystem({
16
+ const context = await core.loadSystem({
17
17
  environment: 'prod',
18
18
  config: config.default(),
19
- }))
19
+ })
20
20
  await context.mcp.mcp.start()
21
21
  }
22
22
 
23
23
  if (esMain(import.meta)) {
24
24
  const args = _parseArguments()
25
- startServer(args.environment).catch((error) => {
25
+ startServer(args.environment).catch(error => {
26
26
  console.error('Failed to start the server:', error)
27
27
  process.exit(1)
28
28
  })
package/config.prod.mjs CHANGED
@@ -1,8 +1,4 @@
1
- import {
2
- CoreNamespace,
3
- LogFormat,
4
- LogLevelNames,
5
- } from '@node-in-layers/core'
1
+ import { CoreNamespace, LogFormat, LogLevelNames } from '@node-in-layers/core'
6
2
  import { McpNamespace } from '@node-in-layers/mcp-server'
7
3
 
8
4
  export default async () => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@node-in-layers/core-knowledge-mcp",
3
- "version": "1.0.0",
3
+ "version": "1.10.10",
4
4
  "description": "A developer MCP server for working with systems using node-in-layers core.",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -11,7 +11,7 @@
11
11
  "dist": "npm run build && cd dist && npm publish"
12
12
  },
13
13
  "dependencies": {
14
- "@node-in-layers/core": "file:..",
14
+ "@node-in-layers/core": "1.10.10",
15
15
  "@node-in-layers/mcp-server": "^2.2.6",
16
16
  "lodash": "^4.17.21",
17
17
  "zod": "^4.1.12"
@@ -58,4 +58,4 @@
58
58
  "tsx": "^4.19.2",
59
59
  "typescript": "5.3.3"
60
60
  }
61
- }
61
+ }
package/bin/build.sh DELETED
@@ -1,30 +0,0 @@
1
- #!/bin/bash
2
- set -e
3
-
4
- # Build parent core package so we can embed it
5
- npm --prefix .. run build
6
-
7
- rm -Rf ./dist
8
-
9
- # Bundle built core into local node_modules for runtime self-containment
10
- rm -Rf ./node_modules/@node-in-layers/core
11
- mkdir -p ./node_modules/@node-in-layers/core
12
- cp -R ../dist/* ./node_modules/@node-in-layers/core/
13
-
14
- npm run tsc -- -p ./tsconfig.json
15
- node - <<'NODE'
16
- const fs = require('fs');
17
- const corePkg = JSON.parse(fs.readFileSync('../package.json','utf8'));
18
- const mcpPkg = JSON.parse(fs.readFileSync('./package.json','utf8'));
19
- mcpPkg.version = corePkg.version;
20
- mcpPkg.dependencies = mcpPkg.dependencies || {};
21
- mcpPkg.dependencies['@node-in-layers/core'] = corePkg.version;
22
- fs.mkdirSync('./dist', { recursive: true });
23
- fs.writeFileSync('./dist/package.json', JSON.stringify(mcpPkg, null, 2));
24
- NODE
25
- cp README.md ./dist
26
- cp -R ./bin/ ./dist/
27
- cp ./config.prod.mjs ./dist/
28
- rm ./dist/bin/build.sh
29
-
30
- sed -i -e 's/..\/dist\//..\//g' ./dist/bin/mcp_server.js
package/dist/README.md DELETED
@@ -1,7 +0,0 @@
1
- # Development Mcp Tool for Node in Layers Core
2
- Whenever you build a system using node in layers core, you can use this MCP tool to teach the AI how to actually interact and work with your node in layers system.
3
-
4
- ## How To Use
5
- ```bash
6
- npx install @node-in-layers/core-knowledge-mcp
7
- ```
@@ -1,29 +0,0 @@
1
- #!/usr/bin/env tsx
2
-
3
- import esMain from 'es-main'
4
- import { ArgumentParser } from 'argparse'
5
- import * as core from '@node-in-layers/core'
6
- import * as config from '../config.prod.mjs'
7
-
8
- const _parseArguments = () => {
9
- const parser = new ArgumentParser({
10
- description: 'Starts the MCP server.',
11
- })
12
- return parser.parse_args()
13
- }
14
-
15
- const startServer = async () => {
16
- const context = (await core.loadSystem({
17
- environment: 'prod',
18
- config: config.default(),
19
- }))
20
- await context.mcp.mcp.start()
21
- }
22
-
23
- if (esMain(import.meta)) {
24
- const args = _parseArguments()
25
- startServer(args.environment).catch((error) => {
26
- console.error('Failed to start the server:', error)
27
- process.exit(1)
28
- })
29
- }
@@ -1,77 +0,0 @@
1
- #!/usr/bin/env tsx
2
-
3
- import invoke from 'lodash/invoke.js'
4
- import esMain from 'es-main'
5
- import { ArgumentParser } from 'argparse'
6
- import repl from 'repl'
7
- import chalk from 'chalk'
8
- import merge from 'lodash/merge.js'
9
- import { queryBuilder } from 'functional-models'
10
- import * as core from '@node-in-layers/core'
11
- import { System } from '../src/types.js'
12
-
13
- const _parseArguments = () => {
14
- const parser = new ArgumentParser({
15
- description: 'Starts a shell environment into the system.',
16
- })
17
- parser.add_argument('environment', {
18
- help: 'The environment for the service.',
19
- })
20
- parser.add_argument('-c', '--command', {
21
- help: 'A dot path command to run',
22
- })
23
- parser.add_argument('-d', '--data', {
24
- help: 'Stringified JSON data to pass to the command',
25
- })
26
- return parser.parse_args()
27
- }
28
-
29
- const _systemStartup = async environment => {
30
- return core.loadSystem({
31
- environment,
32
- }) as unknown as System
33
- }
34
-
35
- const help = objects => () => {
36
- console.info(chalk.white.bold(`You have access to the following objects:`))
37
- console.info(chalk.white.bold(`[${Object.keys(objects).join(', ')}]`))
38
- console.info()
39
- console.info(
40
- chalk.white.bold('You can also write "help()" to see this again.')
41
- )
42
- }
43
-
44
- const runCommand = async (objects, command, data) => {
45
- return invoke(objects, command, data)
46
- }
47
-
48
- const main = async () => {
49
- const args = _parseArguments()
50
- const objects = { ...(await _systemStartup(args.environment)), queryBuilder }
51
- process.on('SIGINT', async function () {
52
- await objects.services['@node-in-layers/data'].cleanup()
53
- process.exit()
54
- })
55
- if (args.command) {
56
- const result = await runCommand(
57
- objects,
58
- args.command,
59
- args.data ? JSON.parse(args.data) : []
60
- )
61
- console.info(result)
62
- process.exit()
63
- return
64
- }
65
- const context = repl.start().context
66
- const toUse = merge({ context: objects }, objects)
67
- merge(context, objects, toUse)
68
- console.info(chalk.blue.bold(`Welcome to the shell.`))
69
- console.info(chalk.blue.bold(`--------------------------------`))
70
- const helpFunc = help(toUse)
71
- helpFunc()
72
- context.help = helpFunc
73
- }
74
-
75
- if (esMain(import.meta)) {
76
- main()
77
- }
@@ -1,61 +0,0 @@
1
- import {
2
- CoreNamespace,
3
- LogFormat,
4
- LogLevelNames,
5
- } from '@node-in-layers/core'
6
- import { McpNamespace } from '@node-in-layers/mcp-server'
7
-
8
- export default async () => {
9
- return {
10
- environment: 'prod',
11
- systemName: '@node-in-layers/core/knowledge-mcp',
12
- [CoreNamespace.root]: {
13
- // @ts-ignore
14
- apps: await Promise.all([
15
- import(`@node-in-layers/mcp-server/index.js`),
16
- import('./mcp/index.js'),
17
- import('./knowledge/index.js'),
18
- ]),
19
- layerOrder: ['services', 'features', ['entries', 'mcp']],
20
- logging: {
21
- logLevel: LogLevelNames.trace,
22
- logFormat: LogFormat.json,
23
- // @ts-ignore
24
- //customLogger: createCustomLogger(),
25
- ignoreLayerFunctions: {
26
- 'amplify:services:getActiveGrowthAds': true,
27
- 'mongo.services.getMongoCollection': true,
28
- 'logs.features.searchRequests': true,
29
- 'auth.features': true,
30
- 'logging.services': true,
31
- 'logging.features': true,
32
- 'deepHelixAuth.services': true,
33
- 'deepHelixAuth.features.authMiddleware': true,
34
- 'mcp.mcp.addTool': true,
35
- 'mcp.mcp.addUnprotectedRoute': true,
36
- 'mcp.mcp.start': true,
37
- 'mcp.mcp.addFeature': true,
38
- '@node-in-layers/data.express': true,
39
- '@node-in-layers/data.services': true,
40
- '@node-in-layers/data.features': true,
41
- '@node-in-layers/rest-api/express.express': true,
42
- '@node-in-layers/rest-api/express.features': true,
43
- '@node-in-layers/rest-api/express.services': true,
44
- '@node-in-layers/mcp-server.mcp': true,
45
- 'azure.services.discoverAzureStorageAccountAndKey': true,
46
- 'tasks.features.runFeatureTask': true,
47
- 'azure.express': true,
48
- 'api.express': true,
49
- },
50
- },
51
- },
52
- [McpNamespace]: {
53
- server: {
54
- connection: {
55
- // @ts-ignore
56
- type: 'cli',
57
- },
58
- },
59
- },
60
- }
61
- }
package/dist/package.json DELETED
@@ -1,61 +0,0 @@
1
- {
2
- "name": "@node-in-layers/core-knowledge-mcp",
3
- "version": "1.10.10",
4
- "description": "A developer MCP server for working with systems using node-in-layers core.",
5
- "main": "index.js",
6
- "type": "module",
7
- "scripts": {
8
- "tsc": "tsc",
9
- "build": "./bin/build.sh",
10
- "build:watch": "nodemon -e '*' --watch ./src --exec ./bin/build.sh",
11
- "dist": "npm run build && cd dist && npm publish"
12
- },
13
- "dependencies": {
14
- "@node-in-layers/core": "1.10.10",
15
- "@node-in-layers/mcp-server": "^2.2.6",
16
- "lodash": "^4.17.21",
17
- "zod": "^4.1.12"
18
- },
19
- "devDependencies": {
20
- "@cucumber/cucumber": "^12.2.0",
21
- "@eslint/compat": "^1.2.0",
22
- "@eslint/eslintrc": "^3.1.0",
23
- "@eslint/js": "^9.12.0",
24
- "@types/chai-as-promised": "^8.0.1",
25
- "@types/json-stringify-safe": "^5.0.3",
26
- "@types/lodash": "^4.17.13",
27
- "@types/mocha": "^10.0.10",
28
- "@types/node": "^22.18.8",
29
- "@types/proxyquire": "^1.3.31",
30
- "@types/sinon": "^17.0.3",
31
- "@typescript-eslint/eslint-plugin": "8.13.0",
32
- "@typescript-eslint/parser": "8.13.0",
33
- "argparse": "^2.0.1",
34
- "c8": "^10.1.3",
35
- "chai": "^5.1.2",
36
- "chai-as-promised": "^8.0.1",
37
- "cz-conventional-changelog": "^3.3.0",
38
- "es-main": "^1.4.0",
39
- "eslint": "9.14.0",
40
- "eslint-config-prettier": "^9.1.0",
41
- "eslint-import-resolver-typescript": "^3.6.3",
42
- "eslint-plugin-functional": "~7.1.0",
43
- "eslint-plugin-import": "^2.31.0",
44
- "esmock": "^2.7.3",
45
- "esprima": "^4.0.1",
46
- "globals": "^15.12.0",
47
- "handlebars": "^4.7.8",
48
- "js-yaml": "^4.1.0",
49
- "mocha": "^10.8.2",
50
- "nodemon": "^3.1.7",
51
- "prettier": "^3.3.3",
52
- "proxyquire": "^2.1.3",
53
- "sinon": "^19.0.2",
54
- "sinon-chai": "^4.0.0",
55
- "source-map-support": "^0.5.21",
56
- "ts-mocha": "^10.0.0",
57
- "ts-node": "^10.4.0",
58
- "tsx": "^4.19.2",
59
- "typescript": "5.3.3"
60
- }
61
- }
@@ -1,215 +0,0 @@
1
- [
2
- {
3
- "id": "7c0c9643-92f4-4603-984d-3a25b9bd3a28",
4
- "name": "Node In Layers: The Only Right Way (Document Overview)",
5
- "description": "Canonical, opinionated, and production-proven guide for building Node In Layers systems. Every step is real, opinionated, and explained for both humans and AI. Deviate at your own risk.\n",
6
- "content": "```markdown\nTo understand a real Node In Layers domain, here is the exact, production-grade implementation of the `@coding` domain. All AI comments are preserved to explain the rationale and standards for each file.\n\n> **Key Standards/AI Callouts:**\n>\n> - Always export each layer (services, features, models) as named exports.\n> - The `name` export must be unique within the system.\n> - Never use default exports for layers—this is how the loader discovers your domain.\n\n```typescript\nconst name = 'coding'\nexport { name }\nexport * as services from './services.js'\nexport * as features from './features.js'\nexport * as models from './models/index.js'\n```\n\n```\n",
7
- "tags": [
8
- "overview",
9
- "canonical",
10
- "best-practices"
11
- ]
12
- },
13
- {
14
- "id": "73cb0b07-7349-4ced-b6c8-8d35ba5a00e0",
15
- "name": "Node In Layers: The Only Right Way (Title & Intro)",
16
- "description": "The main title and introductory statement for the Node In Layers canonical guide.\n",
17
- "content": "```\n# Node In Layers: The Only Right Way\n\n**This is the canonical, must-follow guide for building Node In Layers systems. Every step is real, opinionated, and explained for both humans and AI. Deviate at your own risk.**\n```\n",
18
- "tags": [
19
- "overview",
20
- "canonical",
21
- "best-practices"
22
- ]
23
- },
24
- {
25
- "id": "0b6d5650-c0b6-468a-924d-ffcc60df7b3c",
26
- "name": "How to Create a New Domain (Toolkit Scaffold)",
27
- "description": "Use the official toolkit to create a new domain. This ensures correct structure and discoverability.\n",
28
- "content": "```\n```sh\nnpx @node-in-layers/toolkit create-app coding\n```\n\n/coding\nindex.ts\nservices.ts\nfeatures.ts\ntypes.ts\n/models\nindex.ts\n```\n```\n",
29
- "tags": [
30
- "domains",
31
- "toolkit",
32
- "best-practices"
33
- ]
34
- },
35
- {
36
- "id": "81fdec37-1d24-4784-ba07-ea463bb31265",
37
- "name": "index.ts — Domain Entry Point",
38
- "description": "The entry point for a Node In Layers domain. Exports the domain's layers and unique name for system discovery.\n",
39
- "content": "```markdown\nTo understand a real Node In Layers domain, here is the exact, production-grade implementation of the `@coding` domain. All AI comments are preserved to explain the rationale and standards for each file.\n\n> **Key Standards/AI Callouts:**\n>\n> - Always export each layer (services, features, models) as named exports.\n> - The `name` export must be unique within the system.\n> - Never use default exports for layers—this is how the loader discovers your domain.\n\n```typescript\nconst name = 'coding'\n\nexport { name }\nexport * as services from './services.js'\nexport * as features from './features.js'\nexport * as models from './models/index.js'\n```\n```\n",
40
- "tags": [
41
- "domains",
42
- "architecture",
43
- "best-practices"
44
- ]
45
- },
46
- {
47
- "id": "7fc7f7b4-48b7-4667-a4bc-d96a80fde61f",
48
- "name": "services.ts — Services Layer",
49
- "description": "This file defines the services layer for the domain. Services connect to external resources (file systems, databases, APIs) and are always implemented as a closure factory. This pattern ensures context and configuration are available to every service.\n",
50
- "content": "```\n### 1.2. `services.ts` — Services Layer\n\n**Description:**\nThis file defines the services layer for the domain. Services connect to external resources (file systems, databases, APIs) and are always implemented as a closure factory. This pattern ensures context and configuration are available to every service.\n\n> **Key Standards/AI Callouts:**\n>\n> - Import order: node libraries, third-party libraries, then local libraries.\n> - Always use a named export for the `create` function.\n> - Use the context logger for all logging; always pass `crossLayerProps` for traceability.\n> - Never put business logic here—services are for external connections only.\n\n```typescript\n// AI: We should put node libraries at the top.\nimport fs from 'node:fs'\nimport path from 'node:path'\n// AI: We should then put third party libraries next.\nimport { Config, CrossLayerProps, ServicesContext } from '@node-in-layers/core'\n// AI: Then put our local libraries after that.\nimport { CodingKnowledgeLibraries, CodingServices } from './types.js'\n\n// AI: If we need to use other services, we would add it to this context after config. Then we can do. context.services.otherService.someFunction()\n// AI: Every layer in a system has a create(context) function that is exported. NEVER as a default export. This is how the system knows how to load it.\n/**\n* Create a coding service object.\n* @param context\n* @returns\n*/\nexport const create = (context: ServicesContext<Config>): CodingServices => {\n const readCodeDescriptionFile = async (\n codingLibrary: CodingKnowledgeLibraries,\n crossLayerProps?: CrossLayerProps\n ) => {\n // AI: If you want to log messages within a function use this function. Also pass the crossLayerProps so that logging flows through.\n const log = context.log.getInnerLogger(\n 'readCodeDescriptionFile',\n crossLayerProps\n )\n const filePath = path.join(\n __dirname,\n './libs/templates',\n `${codingLibrary}.md`\n )\n // AI: Use trace for logging within services.\n log.trace('Reading Code Library File', { filePath })\n const file = fs.readFileSync(filePath, 'utf8')\n log.trace('Reading Code Library File', { filePath })\n return file\n }\n\n return {\n readCodeDescriptionFile,\n }\n}\n\n```\n```\n",
51
- "tags": [
52
- "domains",
53
- "services",
54
- "architecture",
55
- "best-practices"
56
- ]
57
- },
58
- {
59
- "id": "21229efb-9d95-48b1-8ce7-0922837c9919",
60
- "name": "features.ts — Features Layer",
61
- "description": "This file defines the features layer for the domain. Features integrate services and implement business logic. They are always implemented as a closure factory and should never connect to external resources directly.\n",
62
- "content": "```\n### 1.3. `features.ts` — Features Layer\n\n**Description:**\nThis file defines the features layer for the domain. Features integrate services and implement business logic. They are always implemented as a closure factory and should never connect to external resources directly.\n\n> **Key Standards/AI Callouts:**\n>\n> - Always use the context logger for all logging; pass `crossLayerProps` for traceability.\n> - Use `error` for critical failures, `debug` for internal feature logic.\n> - Features should only call services, never connect to external resources directly.\n> - Return pure data structures—never mutate context or state.\n\n```typescript\nimport { randomUUID } from 'node:crypto'\nimport {\n Config,\n CrossLayerProps,\n FeaturesContext,\n} from '@node-in-layers/core/index.js'\nimport {\n CodingServicesLayer,\n CodingFeaturesLayer,\n CodingFeatures,\n CodingKnowledgeLibraries,\n} from './types.js'\n\nexport const create = (\n // NOTE: We are going to use the coding services object, so we need to add the layer here to the context which is specific to features.\n context: FeaturesContext<Config, CodingServicesLayer>\n): CodingFeatures => {\n const _availableLibraries = [CodingKnowledgeLibraries.nodeInLayers]\n\n const listAvailableCodeKnowledgeBases = async (\n crossLayerProps?: CrossLayerProps\n ) => {\n return Promise.resolve(_availableLibraries)\n }\n\n // AI: All of these inputs are automatically logged when this function is executed.\n const getCodeLibraryKnowledgeBase = async (\n codingLibrary: CodingKnowledgeLibraries,\n tags?: readonly string[],\n crossLayerProps?: CrossLayerProps\n ) => {\n const log = context.log.getInnerLogger(\n 'getCodeLibraryKnowledgeBase',\n crossLayerProps\n )\n if (_availableLibraries.includes(codingLibrary) === false) {\n // AI: If you have an error that ends execution, use error.\n log.error(`Coding library ${codingLibrary} not found`)\n throw new Error(`Coding library ${codingLibrary} not found`)\n }\n\n // AI: Inside of features, use debug for logging.\n // AI: Btw this log message is not needed. Its just for show.\n log.debug('Reading code library', { codingLibrary })\n // AI: ALWAYS pass the crossLayerProps to other features/services or any other layer call.\n const content = await context.services.coding.readCodeDescriptionFile(\n codingLibrary,\n crossLayerProps\n )\n return [\n {\n id: randomUUID(),\n library: codingLibrary,\n title: `Code Library: ${codingLibrary} details`,\n details: content,\n tags: ['typescript', 'nodejs', codingLibrary, 'how-to', 'examples'],\n lastModified: new Date(),\n createdAt: new Date(),\n },\n ]\n }\n\n return {\n listAvailableCodeKnowledgeBases,\n getCodeLibraryKnowledgeBase,\n }\n}\n```\n\n```\n",
63
- "tags": [
64
- "features",
65
- "typescript",
66
- "nodejs",
67
- "layer",
68
- "business-logic"
69
- ]
70
- },
71
- {
72
- "id": "21d72502-e037-45ba-9c63-5c32bd686f88",
73
- "name": "types.ts — Types and Contracts",
74
- "description": "This file defines all types, enums, and contracts for the domain. Types are always exported at the bottom. AI comments explain the use of Readonly, LayerFunction, and export conventions.\n",
75
- "content": "```\n\n### 1.4. `types.ts` — Types and Contracts\n\n**Description:**\nThis file defines all types, enums, and contracts for the domain. Types are always exported at the bottom. AI comments explain the use of Readonly, LayerFunction, and export conventions.\n\n> **Key Standards/AI Callouts:**\n>\n> - Use `Readonly` for all types to enforce immutability.\n> - Use `LayerFunction` to ensure all functions accept `crossLayerProps` for traceability.\n> - Export all types at the bottom of the file for discoverability.\n\n```typescript\nimport { LayerFunction } from '@node-in-layers/core'\n\nexport enum CodingKnowledgeLibraries {\n nodeInLayers = 'node-in-layers',\n functionalModels = 'functional-models',\n nodeInLayersData = 'node-in-layers-data',\n nodeInLayersExpress = 'node-in-layers-express',\n}\n\n// AI: This is a type, that we will later create a model for, so we can save in a database.\n// AI: Notice that we are using Readonly<{}> for our types.\nexport type CodingKnowledgeBaseEntry = Readonly<{\n id: string\n library: CodingKnowledgeLibraries\n title: string\n details: string\n tags: string[]\n lastModified: Date\n createdAt: Date\n}>\n\n// AI: \"DomainServices\" is the type that is produced by a services factory\nexport type CodingServices = Readonly<{\n // AI: If you use the LayerFunction<> type, it automatically adds the CrossLayerProps to the end of the function. This is needed for tracing throughout the system.\n readCodeDescriptionFile: LayerFunction<\n (codingLibrary: CodingKnowledgeLibraries) => Promise<string>\n >\n}>\n\n// AI: \"DomainServicesLayer\" is the type that another service/feature uses to explicitly say it uses this service\nexport type CodingServicesLayer = Readonly<{\n coding: CodingServices\n}>\n\n// AI: \"DomainFeatures\" is the type that is produced by a features factory\nexport type CodingFeatures = Readonly<{\n // I want to provide the AI the ability to see what code libraries are available to it\n listAvailableCodeKnowledgeBases: LayerFunction<\n () => Promise<CodingKnowledgeLibraries[]>\n >\n // I know that the AI will need the ability to get the specific entries for a library\n getCodeLibraryKnowledgeBase: LayerFunction<\n (\n codingLibrary: CodingKnowledgeLibraries,\n tags?: readonly string[]\n ) => Promise<CodingKnowledgeBaseEntry[]>\n >\n}>\n\n// AI: \"DomainFeaturesLayer\" is the type that another service/feature uses to explicitly say it uses this feature\nexport type CodingFeaturesLayer = Readonly<{\n coding: CodingFeatures\n}>\n\n```\n\n```\n",
76
- "tags": [
77
- "types",
78
- "contracts",
79
- "typescript",
80
- "nodejs",
81
- "layer"
82
- ]
83
- },
84
- {
85
- "id": "9d205a10-b48e-44c6-9dc4-2c18507386ec",
86
- "name": "Model Exports and Persistent Model for Coding Knowledge Base",
87
- "description": "This entry documents the canonical approach for exporting models and defining persistent models in a Node In Layers domain. All models must be exported with their plural name for system discoverability. Persistent models use property creators from `functional-models` to enforce type safety and validation. The `create` function and naming conventions are required for automatic model loading.\n",
88
- "content": "### 1.5. `models/index.ts` — Model Exports\n**Description:**\nThis file exports all models for the domain. Each model must be exported with its plural name for system discoverability.\n\n> **Key Standards/AI Callouts:**\n> - Always export each model with its plural name.\n> - This pattern ensures all models are discoverable and loadable by the system.\n\n```typescript\n// We have to export each and every model with the plural name of the model.\nexport * as CodingKnowledgeBaseEntries from './CodingKnowledgeBaseEntries.js'\n```\n\n### 1.6. `models/CodingKnowledgeBaseEntries.ts` — Persistent Model\n\n**Description:**\nThis file defines the persistent model for knowledge base entries. It uses property creators from `functional-models` to enforce type safety and validation. The `create` function and naming conventions are required by Node In Layers for automatic model loading.\n\n> **Key Standards/AI Callouts:**\n>\n> - Always use property creators for each field to enforce type safety and validation.\n> - The `create` function must be named and exported for system loading.\n> - Use plural and singular names for models to avoid naming clashes.\n\n```typescript\nimport {\n TextProperty,\n DatetimeProperty,\n PrimaryKeyUuidProperty,\n ArrayProperty,\n} from 'functional-models'\nimport { ModelProps } from '@node-in-layers/core'\nimport { CodingKnowledgeBaseEntry, CodingKnowledgeLibraries } from '../types.js'\n\n/**\n * Database Model for Coding Knowledge Base Entries\n * This create function is required to be called create() so that the system can load it automatically.\n * NOTE: This model does not have any foreign keys.\n * You have to use the fetcher and getModel functions to link to other models. I have them just so you can see them.\n */\nexport const create = ({ Model, fetcher, getModel }: ModelProps) => {\n return Model<CodingKnowledgeBaseEntry>({\n // This is a required field.\n pluralName: 'CodingKnowledgeBaseEntries',\n // (Optional): But its useful to force the correct creation of a singular name. It does do it automatically behind the scenes most of the time.\n singularName: 'CodingKnowledgeBaseEntry',\n // The namespace should be at least the domain name, but in some cases you may have a namespace+domain if you are worried about clashes\n namespace: 'coding',\n // NOTE: This is not necessary if we use the value id. I put it here so you can see it.\n primaryKeyName: 'id',\n // Each of the type's properties are defined here. There is a whole library of properties in the functional-models library.\n properties: {\n // This creates a uuid automatically for the id.\n id: PrimaryKeyUuidProperty(),\n library: TextProperty({\n required: true,\n choices: Object.values(CodingKnowledgeLibraries),\n }),\n title: TextProperty({ required: true }),\n details: TextProperty({ required: true }),\n tags: ArrayProperty<string>(),\n lastModified: DatetimeProperty({ autoNow: true }),\n createdAt: DatetimeProperty({ autoNow: true }),\n },\n })\n}\n\n```\n",
89
- "tags": [
90
- "models",
91
- "exports",
92
- "persistence",
93
- "best-practices"
94
- ]
95
- },
96
- {
97
- "id": "9707d819-ac03-4f82-87a7-0750d95a1530",
98
- "name": "Minimal Real-World config.base.mts Example",
99
- "description": "This entry provides a real, production-ready minimal configuration example for Node In Layers systems. Use this as your starting point for any Node In Layers system. It demonstrates how to organize domains, layers, logging, databases, and environment-safe configuration.\n",
100
- "content": "\n### 2.1. Minimal (Real-World) `config.base.mts` Example\n\nThis is a real, production-ready minimal config. Use this as your starting point for any Node In Layers system.\n\n```typescript\n// AI:\nimport { CoreNamespace, LogFormat } from '@node-in-layers/core/index.js'\n// AI: We should only use this if we are dealing with a backend system.\nimport { DataNamespace } from '@node-in-layers/data/index.js'\n// AI: We should only use RestApi if we have an express server.\nimport { RestApiNamespace } from '@node-in-layers/rest-api/index.js'\n\nimport { createCustomLogger } from '@deep-helix/backend-sdk'\nimport { SystemConfig } from './src/types'\nimport { LogLevelNames } from '@node-in-layers/core'\nimport { SimpleServerConfig } from '@l4t/mcp-ai'\n\nconst core = {\n // AI: The domains are loaded here in order from top to bottom.\n apps: await Promise.all([\n // AI: Only need if we use data.\n import(`@node-in-layers/data/index.js`),\n import(`@node-in-layers/rest-api/express/index.js`),\n // AI: We created some helpful logging functionalities.\n import(`@deep-helix/backend-sdk/dist/logging/index.js`),\n // AI: This is just an example app\n import(`./dist/coding/index.js`),\n import(`./dist/mcp-server/index.js`),\n // AI: This is just an example app for an api express server.\n import(`./dist/api/index.js`),\n ]),\n // AI: The layers are loaded from left to right, except when its an array. That is a composite layer.\n layerOrder: ['services', 'features', ['entries', 'express']],\n logging: {\n logLevel: LogLevelNames.trace,\n logFormat: LogFormat.json,\n customLogger: createCustomLogger(),\n },\n // AI: This sets the default model factory for database backends..\n modelFactory: '@node-in-layers/data',\n // AI: This should probably always be true. It automatically creates CRUD operations for all models.\n modelCruds: true,\n // AI: This is an example of creating multi-database systems, where for this situation the logging app uses a different database than the main app, note \"logDatabase\"\n customModelFactory: {\n // AI: Domain\n ['logging']: {\n // AI: The model's name: ['namespace', 'databaseNameListedBelow']\n LogMessages: ['@node-in-layers/data', 'logDatabase'],\n },\n },\n}\n\n// AI: This is for @node-in-layers/data.\nconst data = {\n databases: {\n default: {\n datastoreType: 'memory', // AI: mongo, postgres, etc.\n },\n // AI: Multi-database configuration\n logDatabase: {\n datastoreType: 'memory',\n },\n },\n}\n\nconst logging = {\n colorized: false,\n databaseLogging: false,\n consoleLogging: true,\n}\n\nconst express = {\n port: 3000,\n urlPrefix: '/api/v1/',\n logging: {\n requestLogLevel: LogLevelNames.info,\n responseLogLevel: LogLevelNames.info,\n },\n jsonBodySizeLimitInMb: 10,\n encodedBodySizeLimitInMb: 10,\n}\n\nconst mcpServer: Omit<SimpleServerConfig, 'tools'> = {\n name: 'my-mcp-server',\n version: '1.0.0',\n server: {\n connection: {\n type: 'cli',\n },\n },\n}\n\n// AI: Actually create the config as a default exported function\nexport default (): SystemConfig => ({\n // AI: Override this.\n environment: 'base',\n // AI: The name of our system.\n systemName: 'praxis-sphere/backend',\n [CoreNamespace.root]: core,\n [DataNamespace.root]: data,\n [RestApiNamespace.express]: express,\n logging,\n mcpServer,\n})\n```\n\n**AI CALLOUT:** Real systems require detailed config for logging, databases, APIs, and more. Use this as your reference for production-grade systems. Always keep config organized, DRY, and environment-safe.\n",
101
- "tags": [
102
- "config",
103
- "base",
104
- "best-practices"
105
- ]
106
- },
107
- {
108
- "id": "97c2d711-7bed-4426-b270-c81af4ed58e8",
109
- "name": "Environment-Specific Config Inheritance and Overrides",
110
- "description": "This entry describes the canonical pattern for environment-specific configuration in Node In Layers systems. Always clone and merge the base config for environment overrides. This prevents accidental mutation, keeps your config DRY, and makes it easy to add or change only what you need for each environment. Never put secrets or credentials in committed config files.\n",
111
- "content": "\n### 2.2. Inheritance/Overrides\n\nFor environment-specific config, use a robust pattern that clones and merges the base config. This ensures you never mutate the base config and can safely override only what you need.\n\n```typescript\n// config.dev.mts\nimport merge from 'lodash/merge.js'\nimport cloneDeep from 'lodash/cloneDeep.js'\nimport { DataNamespace } from '@node-in-layers/data/index.js'\nimport * as config from './config.base.mts'\nimport { SystemConfig } from './src/types'\n\nexport default (): SystemConfig => {\n const instance = config.default()\n const cloned = cloneDeep(instance)\n return merge(cloned, {\n environment: 'dev',\n [DataNamespace.root]: {\n databases: {\n default: {\n datastoreType: 'mongo',\n host: 'localhost',\n port: 27017,\n },\n logDatabase: {\n // Example: override or add dev-specific database config here\n },\n },\n },\n logging: {\n colorized: true,\n databaseLogging: true,\n },\n })\n}\n```\n\n> **AI CALLOUT:** Always clone and merge the base config for environment overrides. This pattern prevents accidental mutation, keeps your config DRY, and makes it easy to add or change only what you need for each environment. Never put secrets or credentials in committed config files.\n",
112
- "tags": [
113
- "config",
114
- "inheritance",
115
- "overrides",
116
- "best-practices"
117
- ]
118
- },
119
- {
120
- "id": "e1c6daad-2e78-48ab-a98a-adc8318cd293",
121
- "name": "Using loadSystem with Real Config",
122
- "description": "This entry explains how to bootstrap a Node In Layers system using the `loadSystem` function and a real config. Always call the config as a function (e.g., `config()`). Never manually wire up layers or context. `loadSystem` ensures everything is loaded in the correct order, with full traceability and logging.\n",
123
- "content": "### 3.1. Using `loadSystem` with Real Config\n\nTo bootstrap your Node In Layers system, use the `loadSystem` function from `@node-in-layers/core`. This will automatically load all domains, services, features, and configure logging and context, using the real config structure above.\n\n```typescript\n// src/main.ts\nimport config from '../config.base.mts'\nimport { loadSystem } from '@node-in-layers/core'\n\nconst system = await loadSystem({ config: config() })\n```\n\n> **AI CALLOUT:** Always call the config as a function (e.g., `config()`). Never manually wire up layers or context. `loadSystem` ensures everything is loaded in the correct order, with full traceability and logging.\n",
124
- "tags": [
125
- "system",
126
- "bootstrap",
127
- "config",
128
- "best-practices"
129
- ]
130
- },
131
- {
132
- "id": "49487af8-6b6a-4fb7-b1f7-9b1358eef006",
133
- "name": "What Gets Wired Up Automatically by loadSystem",
134
- "description": "This entry lists everything that is automatically wired up by the `loadSystem` function in Node In Layers. This automation prevents misconfiguration and reduces boilerplate. Always use the system loader. If you skip this, you will break traceability, logging, and system conventions.\n",
135
- "content": "### 3.2. What Gets Wired Up Automatically\n\n- All domains and their layers (models, services, features, entries)\n- Context propagation (config, loggers, crossLayerProps)\n- Logging setup for all layers\n- Model factories and CRUDs if enabled\n\n**AI CALLOUT:** This automation prevents misconfiguration and reduces boilerplate. Always use the system loader. If you skip this, you will break traceability, logging, and system conventions.\n",
136
- "tags": [
137
- "system",
138
- "automation",
139
- "wiring",
140
- "best-practices"
141
- ]
142
- },
143
- {
144
- "id": "c366c193-223e-4a0e-9a72-6f4c4f1b9f94",
145
- "name": "Minimal CLI Entry Example",
146
- "description": "This entry provides a minimal, real-world example of a CLI entry file for Node In Layers systems. Entries are the only place you should handle CLI args, environment, or direct user interaction. Keep them thin—just grab args, call a feature, and log output. Never put business logic in entries. They should only handle input/output and call features. This keeps your system testable and maintainable. Always use `console.info` for meaningful outputs, and use the logger for internal details.\n",
147
- "content": "### Entry/Bin Minimal CLI Entry Example\n\nEntries are the only place you should handle CLI args, environment, or direct user interaction. Keep them thin—just grab args, call a feature, and log output. Here is a real example:\n\n```typescript\n// bin/coding.ts\n#!/usr/bin/env tsx\nimport { loadSystem } from '@node-in-layers/core'\nimport config from '../config.base.mts'\n\nconst main = async () => {\n const system = await loadSystem({ config: config() })\n // Example: Call a feature and print the result\n const result = await system.features.coding.getCodeLibraryKnowledgeBase('node-in-layers')\n console.info(JSON.stringify(result, null, 2))\n}\n\nmain()\n```\n\n> **AI CALLOUT:** Never put business logic in entries. They should only handle input/output and call features. This keeps your system testable and maintainable. Always use `console.info` for meaningful outputs, and use the logger for internal details.\n",
148
- "tags": [
149
- "entries",
150
- "cli",
151
- "best-practices"
152
- ]
153
- },
154
- {
155
- "id": "03a2732c-408a-4332-8fbe-9089c93ce9b5",
156
- "name": "Advanced CLI Entry Example",
157
- "description": "This entry provides a real, production-grade CLI entry file for a Node In Layers system. It demonstrates argument parsing, system loading, feature invocation, and robust error handling. Use a shebang and `tsx` for TypeScript executables. Use a main function and check for CLI invocation with `es-main`. Parse arguments with a library (e.g., `argparse`) for clarity and safety. Load the system using the correct config/environment. Call features only—never put business logic in the entry. Use `console.info`/`console.error` for user-facing output; handle errors and exit codes explicitly.\n",
158
- "content": "### 4.2. Real-World Example: Advanced CLI Entry\n\n**Description:**\nThis is a real, production-grade CLI entry file for a YouTube transcription process. It demonstrates argument parsing, system loading, feature invocation, and robust error handling.\n\n> **Key Standards/AI Callouts:**\n>\n> - Use a shebang and `tsx` for TypeScript executables.\n> - Use a main function and check for CLI invocation with `es-main`.\n> - Parse arguments with a library (e.g., `argparse`) for clarity and safety.\n> - Load the system using the correct config/environment.\n> - Call features only—never put business logic in the entry.\n> - Use `console.info`/`console.error` for user-facing output; handle errors and exit codes explicitly.\n\n```typescript\n#!/usr/bin/env tsx\n\nimport esMain from 'es-main'\nimport { ArgumentParser } from 'argparse'\nimport * as core from '@node-in-layers/core'\nimport { SystemConfig } from '../src/types.js'\nimport { System } from '../src/system/types.js'\n\n// AI: Use argparse.\nconst _parseArguments = () => {\n const parser = new ArgumentParser({\n description: 'Executes YouTube transcription process for a given job ID.',\n })\n parser.add_argument('environment', {\n help: 'The environment for the service.',\n })\n parser.add_argument('jobId', {\n help: 'The ID of the transcription job to process.',\n })\n return parser.parse_args()\n}\n\nconst main = async () => {\n const args = _parseArguments()\n\n console.info(`Starting YouTube transcription process for job: ${args.jobId}`)\n\n // AI: Load the system, and cast it as a system object, which shows the types of the entire system.\n const context = (await core.loadSystem<SystemConfig>({\n environment: args.environment,\n })) as unknown as System\n\n try {\n // AI: This runs a feature.\n await context.features.pipelines.executeYoutubeTranscription(args.jobId)\n\n console.info(\n `YouTube transcription completed successfully for job: ${args.jobId}`\n )\n } catch (error) {\n console.error('Error executing YouTube transcription:', error)\n process.exit(1)\n } finally {\n // AI: Some databases will hold a connection open.\n context.services['@node-in-layers/data'].cleanup()\n }\n}\n\nif (esMain(import.meta)) {\n main()\n}\n```\n",
159
- "tags": [
160
- "entries",
161
- "cli",
162
- "advanced",
163
- "best-practices"
164
- ]
165
- },
166
- {
167
- "id": "15b68d0d-9f62-420c-97a4-c7a1ca4c1b7b",
168
- "name": "Deep Dives and AI Callouts",
169
- "description": "This entry summarizes the most important AI callouts and common pitfalls for Node In Layers systems. At the end of each section, review the AI callouts and rationale. Common pitfalls include: not using the toolkit for domain creation, committing secrets or environment-specific config, manually wiring up layers instead of using `loadSystem`, putting business logic in entries, not using function export for config, and not using the correct namespace keys. If you are an AI, always check the knowledge base first, follow this guide exactly, and map code to layers even if the repo uses different terms. Every deviation increases the risk of subtle bugs and system breakage. This document is your source of truth for Node In Layers systems.\n",
170
- "content": "## 5. Deep Dives and AI Callouts\n\nAt the end of each section, review the AI callouts and rationale. Common pitfalls include:\n\n- Not using the toolkit for domain creation\n- Committing secrets or environment-specific config\n- Manually wiring up layers instead of using `loadSystem`\n- Putting business logic in entries\n- Not using function export for config\n- Not using the correct namespace keys\n\n**AI CALLOUT:** If you are an AI, always check the knowledge base first, follow this guide exactly, and map code to layers even if the repo uses different terms. Every deviation increases the risk of subtle bugs and system breakage. This document is your source of truth for Node In Layers systems.\n",
171
- "tags": [
172
- "ai",
173
- "callouts",
174
- "pitfalls",
175
- "best-practices"
176
- ]
177
- },
178
- {
179
- "id": "9e2cd198-cf32-45d3-8bb9-b9773cf4e452",
180
- "name": "All Exported Types in @node-in-layers/core",
181
- "description": "All types exported from @node-in-layers/core, grouped by purpose. Minimal comments, full TypeScript code for reference.\n",
182
- "content": "```typescript\n// --- Config and Core ---\ntype Config = Readonly<{\n systemName: string\n environment: string\n [CoreNamespace.root]: CoreConfig\n}>\ntype CoreConfig = Readonly<{\n logging: {\n logLevel: LogLevelNames\n logFormat: LogFormat | readonly LogFormat[]\n tcpLoggingOptions?: Readonly<{\n url: string\n headers?: Record<string, string | object>\n }>\n customLogger?: RootLogger\n getFunctionWrapLogLevel?: (\n layerName: string,\n functionName?: string\n ) => LogLevelNames\n ignoreLayerFunctions?: Record<\n string,\n Record<string, Record<string, boolean> | boolean>\n >\n }\n layerOrder: readonly LayerDescription[]\n apps: readonly App[]\n modelFactory?: string\n modelCruds?: boolean\n customModelFactory?: NamespaceToFactory\n}>\nenum CoreNamespace {\n root = '@node-in-layers/core',\n globals = '@node-in-layers/core/globals',\n layers = '@node-in-layers/core/layers',\n models = '@node-in-layers/core/models',\n}\ntype App = Readonly<{\n name: string\n services?: AppLayer<Config, any>\n features?: AppLayer<Config, any>\n globals?: GlobalsLayer<Config, any>\n models?: Record<string, ModelConstructor>\n}>\ntype AppLayer<\n TConfig extends Config = Config,\n TContext extends object = object,\n> = Readonly<{\n create: (context: LayerContext<TConfig, TContext>) => MaybePromise<TLayer>\n}>\ntype LayerContext<\n TConfig extends Config = Config,\n TContext extends object = object,\n> = CommonContext<TConfig> & TContext & { log: LayerLogger }\ntype CommonContext<TConfig extends Config = Config> = Readonly<{\n config: TConfig\n rootLogger: RootLogger\n constants: {\n environment: string\n workingDirectory: string\n runtimeId: string\n }\n}>\n\n// --- Logging ---\nenum LogLevel {\n TRACE = 0,\n DEBUG = 1,\n INFO = 2,\n WARN = 3,\n ERROR = 4,\n SILENT = 5,\n}\nenum LogLevelNames {\n trace = 'trace',\n debug = 'debug',\n info = 'info',\n warn = 'warn',\n error = 'error',\n silent = 'silent',\n}\nenum LogFormat {\n json = 'json',\n custom = 'custom',\n simple = 'simple',\n tcp = 'tcp',\n full = 'full',\n}\ntype Logger = Readonly<{\n trace: (\n message: string,\n dataOrError?: Record<string, JsonAble | object> | ErrorObject\n ) => MaybePromise<void>\n debug: (\n message: string,\n dataOrError?: Record<string, JsonAble | object> | ErrorObject\n ) => MaybePromise<void>\n info: (\n message: string,\n dataOrError?: Record<string, JsonAble | object> | ErrorObject\n ) => MaybePromise<void>\n warn: (\n message: string,\n dataOrError?: Record<string, JsonAble | object> | ErrorObject\n ) => MaybePromise<void>\n error: (\n message: string,\n dataOrError?: Record<string, JsonAble | object> | ErrorObject\n ) => MaybePromise<void>\n applyData: (data: Record<string, JsonAble>) => Logger\n getIdLogger: (name: string, logIdorKey: LogId | string, id?: string) => Logger\n getSubLogger: (name: string) => Logger\n getIds: () => readonly LogId[]\n}>\ntype LayerLogger = Logger &\n Readonly<{\n _logWrap: <T, A extends Array<any>>(\n functionName: string,\n func: LogWrapAsync<T, A> | LogWrapSync<T, A>\n ) => (...a: A) => Promise<T> | T\n _logWrapAsync: <T, A extends Array<any>>(\n functionName: string,\n func: LogWrapAsync<T, A>\n ) => (...a: A) => Promise<T>\n _logWrapSync: <T, A extends Array<any>>(\n functionName: string,\n func: LogWrapSync<T, A>\n ) => (...a: A) => T\n getFunctionLogger: (\n name: string,\n crossLayerProps?: CrossLayerProps\n ) => FunctionLogger\n getInnerLogger: (\n functionName: string,\n crossLayerProps?: CrossLayerProps\n ) => FunctionLogger\n }>\ntype AppLogger = Logger &\n Readonly<{\n getLayerLogger: (\n layerName: CommonLayerName | string,\n crossLayerProps?: CrossLayerProps\n ) => LayerLogger\n }>\ntype RootLogger<TConfig extends Config = Config> = Readonly<{\n getLogger: (\n context: CommonContext<TConfig>,\n props?: { ids?: readonly LogId[]; data?: Record<string, any> }\n ) => HighLevelLogger\n}>\ntype HighLevelLogger = Logger & Readonly<{ getAppLogger: GetAppLogger }>\ntype GetAppLogger = (appName: string) => AppLogger\ntype LogId = Readonly<Record<string, string>>\ntype LogMessage<T extends Record<string, JsonAble> = Record<string, JsonAble>> =\n Readonly<{\n id: string\n logger: string\n environment: string\n ids?: readonly LogId[]\n logLevel: LogLevelNames\n datetime: Date\n message: string\n }> &\n Partial<ErrorObject> &\n T\ntype LogFunction = (logMessage: LogMessage) => void | Promise<void>\ntype LogMethod<TConfig extends Config = Config> = (\n context: CommonContext<TConfig>\n) => LogFunction\nenum CommonLayerName {\n models = 'models',\n services = 'services',\n features = 'features',\n entries = 'entries',\n}\n\n// --- Layers and Cross-Layer ---\ntype CrossLayerProps<T extends object = object> = Readonly<{\n logging?: { ids?: readonly LogId[] }\n}> &\n T\ntype LayerFunction<T extends (...args: any[]) => any> = T extends (\n ...args: infer Args\n) => infer ReturnType\n ? (...args: [...Args, crossLayerProps?: CrossLayerProps]) => ReturnType\n : never\ntype TypedFunction<T, A extends Array<any>> = (...args: A) => T\ntype TypedFunctionAsync<T, A extends Array<any>> = (...args: A) => Promise<T>\ntype LogWrapSync<T, A extends Array<any>> = (\n functionLogger: FunctionLogger,\n ...args: A\n) => T\ntype LogWrapAsync<T, A extends Array<any>> = (\n functionLogger: FunctionLogger,\n ...args: A\n) => Promise<T>\ntype FunctionLogger = Logger\ntype GlobalsLayer<\n TConfig extends Config = Config,\n TGlobals extends object = object,\n> = Readonly<{ create: (context: CommonContext<TConfig>) => Promise<TGlobals> }>\ntype ServicesContext<\n TConfig extends Config = Config,\n TServices extends object = object,\n TContext extends object = object,\n> = LayerContext<\n TConfig,\n {\n models: Record<\n string,\n {\n getModels: <TModelType extends ModelType<any>>() => Record<\n string,\n TModelType\n >\n }\n >\n services: TServices\n } & TContext\n>\ntype ServicesLayerFactory<\n TConfig extends Config = Config,\n TServices extends object = object,\n TContext extends object = object,\n TLayer extends object = object,\n> = Readonly<{\n create: (context: ServicesContext<TConfig, TServices, TContext>) => TLayer\n}>\ntype FeaturesContext<\n TConfig extends Config = Config,\n TServices extends object = object,\n TFeatures extends object = object,\n TGlobals extends object = object,\n> = LayerContext<\n TConfig,\n { services: TServices; features: TFeatures } & TGlobals\n>\ntype FeaturesLayerFactory<\n TConfig extends Config = Config,\n TContext extends object = object,\n TServices extends object = object,\n TFeatures extends object = object,\n TLayer extends object = object,\n> = Readonly<{\n create: (\n context: FeaturesContext<TConfig, TServices, TFeatures, TContext>\n ) => TLayer\n}>\ntype System<\n TConfig extends Config = Config,\n TServices extends object = object,\n TFeatures extends object = object,\n> = CommonContext<TConfig> & { services: TServices; features: TFeatures }\n\n// --- Models and CRUD ---\ntype ModelConstructor = Readonly<{\n create: <\n T extends DataDescription,\n TModelExtensions extends object = object,\n TModelInstanceExtensions extends object = object,\n >(\n modelProps: ModelProps\n ) => ModelType<T, TModelExtensions, TModelInstanceExtensions>\n}>\ntype ModelProps<\n TModelOverrides extends object = object,\n TModelInstanceOverrides extends object = object,\n> = Readonly<{\n Model: ModelFactory<TModelOverrides, TModelOverrides>\n fetcher: ModelInstanceFetcher<TModelOverrides, TModelInstanceOverrides>\n getModel: <T extends DataDescription>(\n namespace: string,\n modelName: string\n ) => () => ModelType<T, TModelOverrides, TModelInstanceOverrides>\n}>\ntype PartialModelProps<\n TModelOverrides extends object = object,\n TModelInstanceOverrides extends object = object,\n> = Readonly<{\n Model: ModelFactory<TModelOverrides, TModelOverrides>\n fetcher: ModelInstanceFetcher<TModelOverrides, TModelInstanceOverrides>\n}>\ntype GetModelPropsFunc = (\n context: ServicesContext,\n ...args: any[]\n) => PartialModelProps\ntype GenericLayer = Record<string, any>\ntype LayerServices = Readonly<{\n getModelProps: (context: ServicesContext) => ModelProps\n loadLayer: (\n app: App,\n layer: string,\n existingLayers: LayerContext\n ) => MaybePromise<GenericLayer | undefined>\n}>\ntype LayerServicesLayer = {\n log: LayerLogger\n services: { [CoreNamespace.layers]: LayerServices }\n}\ntype LayerDescription = string | readonly string[]\ntype ModelToModelFactoryNamespace = Record<string, string | [string, any[]]>\ntype NamespaceToFactory = Record<string, ModelToModelFactoryNamespace>\n\n// --- Error and Utility ---\ntype ErrorObject = Readonly<{\n error: Readonly<{\n code: string\n message: string\n details?: string\n data?: Record<string, JsonAble>\n trace?: string\n cause?: ErrorObject\n }>\n}>\ntype MaybePromise<T> = Promise<T> | T\n\n// --- Model CRUDs (from models/types.ts) ---\ntype ModelCrudsFunctions<\n TData extends DataDescription,\n TModelExtensions extends object = object,\n TModelInstanceExtensions extends object = object,\n> = Readonly<{\n getModel: () => OrmModel<TData, TModelExtensions, TModelInstanceExtensions>\n create: CreateFunction<TData>\n retrieve: RetrieveFunction<TData>\n update: UpdateFunction<TData>\n delete: DeleteFunction\n search: SearchFunction<TData>\n}>\ntype ModelServices = Readonly<{\n createModelCruds: <TData extends DataDescription>(\n model: OrmModel<TData>,\n options?: CrudsOptions<TData>\n ) => ModelCrudsFunctions<TData>\n}>\ntype CreateFunction<TData extends DataDescription> = <\n IgnoreProperties extends string = '',\n>(\n data: Omit<TData, IgnoreProperties> | ToObjectResult<TData>\n) => Promise<OrmModelInstance<TData>>\ntype RetrieveFunction<TData extends DataDescription> = (\n primaryKey: PrimaryKeyType\n) => Promise<OrmModelInstance<TData> | undefined>\ntype UpdateFunction<TData extends DataDescription> = (\n primaryKey: PrimaryKeyType,\n data: TData | ToObjectResult<TData>\n) => Promise<OrmModelInstance<TData>>\ntype DeleteFunction = (primaryKey: PrimaryKeyType) => Promise<void>\ntype SearchFunction<TData extends DataDescription> = (\n ormSearch: OrmSearch\n) => Promise<OrmSearchResult<TData>>\ntype CrudsOverrides<TData extends DataDescription> = Partial<\n Omit<ModelCrudsFunctions<TData>, 'getModel'>\n>\ntype CrudsOptions<TData extends DataDescription> = Readonly<{\n overrides?: CrudsOverrides<TData>\n}>\ntype ModelCrudsServicesContext<\n TModels extends Record<string, ModelCrudsFunctions<any>>,\n TConfig extends Config = Config,\n TServices extends object = object,\n TContext extends object = object,\n> = ServicesContext<TConfig, TServices & { cruds: TModels }, TContext>\n```\n",
183
- "tags": [
184
- "types",
185
- "architecture",
186
- "models",
187
- "config",
188
- "domains"
189
- ]
190
- },
191
- {
192
- "id": "9f70590a-3ed8-445a-a483-e2c04a93a642",
193
- "name": "API Surface of @node-in-layers/core",
194
- "description": "All functions and objects exported from @node-in-layers/core. Each export shows its name, input/output types, and a minimal code snippet. Implementation details are omitted.\n",
195
- "content": "```typescript\n// From index.ts\nexport { loadSystem } from './entries.js'\nexport * from './types.js'\nexport * from './models/types.js'\nexport * from './globals/logging.js'\n\n// --- Main Function ---\n// entries.ts\nasync function loadSystem<TConfig extends Config = Config>(args: {\n environment: string\n config?: TConfig\n}): Promise<any>\n\n// --- Logging (globals/logging.ts) ---\nfunction standardLogger<TConfig extends Config = Config>(): RootLogger<TConfig>\nfunction compositeLogger<TConfig extends Config = Config>(\n logMethods: readonly LogMethod<TConfig>[]\n): RootLogger<TConfig>\nfunction consoleLogSimple(logMessage: LogMessage): void\nfunction consoleLogJson(logMessage: LogMessage): void\nfunction consoleLogFull(logMessage: LogMessage): void\nfunction logTcp(\n context: CommonContext\n): (logMessage: LogMessage) => Promise<any>\n\n// --- Types and Model CRUDs (see types entry for full list) ---\n// All types from types.ts and models/types.ts are exported\n```\n",
196
- "tags": [
197
- "api-surface",
198
- "architecture",
199
- "best-practices"
200
- ]
201
- },
202
- {
203
- "id": "c1c4f0c9-annotated-functions-zod4",
204
- "name": "Build Features the Right Way (Annotated Functions, Zod 4 — Highly Encouraged)",
205
- "description": "Highly encouraged: build feature functions with annotatedFunction and Zod 4 for type-safe args/returns, cross-layer tracing, and consistent surfaces for OpenAPI/MCP.",
206
- "content": "### Build Features the Right Way (Annotated Functions, Zod 4 — Highly Encouraged)\n\nIt’s highly encouraged that feature-level functions use `annotatedFunction` with Zod 4. This provides:\n- Typed args/returns with runtime validation (Zod 4)\n- Cross-layer tracing via the final optional `crossLayerProps`\n- Consistent, discoverable function metadata for documentation and MCP tools\n\n```typescript\nimport { annotatedFunction, FeaturesContext, Config } from '@node-in-layers/core'\nimport { z } from 'zod' // Zod v4\n\nexport const create = (context: FeaturesContext<Config, any>) => {\n const hello = annotatedFunction(\n {\n functionName: 'hello',\n domain: 'greetings',\n description: 'Greets a user by name.',\n args: z.object({ name: z.string() }),\n returns: z.object({ greeting: z.string() }),\n },\n (args, crossLayerProps) => {\n const log = context.log.getInnerLogger('hello', crossLayerProps)\n log.info('Greeting user', { name: args.name })\n return { greeting: `Hello ${args.name}` }\n }\n )\n\n return { hello }\n}\n```\n\n> Use Zod 4 explicitly. Using `annotatedFunction` is highly encouraged for building features the right way.",
207
- "tags": [
208
- "features",
209
- "zod4",
210
- "annotatedFunction",
211
- "best-practices",
212
- "mcp"
213
- ]
214
- }
215
- ]
@@ -1,47 +0,0 @@
1
- import {
2
- Config,
3
- FeaturesContext,
4
- annotatedFunction,
5
- NilFunction,
6
- } from '@node-in-layers/core'
7
- import {
8
- KnowledgeServicesLayer,
9
- KnowledgeFeaturesLayer,
10
- GetKnowledgeProps,
11
- GetKnowledgeOutput,
12
- KnowledgeEntrySchema,
13
- KnowledgeFeatures,
14
- } from './types.js'
15
- import { z } from 'zod'
16
- import entries from './entries.json' with { type: 'json' }
17
-
18
- export const create = (
19
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
20
- context: FeaturesContext<
21
- Config,
22
- KnowledgeServicesLayer,
23
- KnowledgeFeaturesLayer
24
- >
25
- ): KnowledgeFeatures => {
26
- const getKnowledgeEntries = annotatedFunction<
27
- GetKnowledgeProps,
28
- GetKnowledgeOutput
29
- >(
30
- {
31
- functionName: 'getKnowledgeEntries',
32
- domain: 'knowledge',
33
- description: 'Explains how to use the @node-in-layers/core library.',
34
- args: z.object({}),
35
- returns: z.object({
36
- knowledge: z.array(KnowledgeEntrySchema),
37
- }),
38
- },
39
- () => ({
40
- knowledge: entries,
41
- })
42
- )
43
-
44
- return {
45
- getKnowledgeEntries,
46
- }
47
- }
@@ -1,6 +0,0 @@
1
- const name = 'knowledge'
2
-
3
- export * as services from './services.js'
4
- export * as features from './features.js'
5
- export * as mcp from './mcp.js'
6
- export { name }
@@ -1,11 +0,0 @@
1
- import { Config } from '@node-in-layers/core'
2
- import { McpContext } from '@node-in-layers/mcp-server'
3
- import { McpMcpLayer } from '../mcp/types'
4
- import { KnowledgeFeaturesLayer } from './types'
5
-
6
- export const create = (
7
- context: McpContext<Config, KnowledgeFeaturesLayer, McpMcpLayer>
8
- ) => {
9
- context.mcp.mcp.addFeature(context.features.knowledge.getKnowledgeEntries)
10
- return {}
11
- }
@@ -1,8 +0,0 @@
1
- import { ServicesContext } from '@node-in-layers/core'
2
-
3
- import { KnowledgeServices } from './types.js'
4
-
5
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
6
- export const create = (context: ServicesContext): KnowledgeServices => {
7
- return {}
8
- }
@@ -1,43 +0,0 @@
1
- import { NilAnnotatedFunction } from '@node-in-layers/core'
2
- import { z } from 'zod'
3
-
4
- export type KnowledgeServices = Readonly<object>
5
-
6
- export type KnowledgeServicesLayer = Readonly<{
7
- knowledge: KnowledgeServices
8
- }>
9
-
10
- export type GetKnowledgeProps = Readonly<{
11
- // Can filter later on
12
- }>
13
-
14
- export type GetKnowledgeOutput = Readonly<{
15
- knowledge: readonly KnowledgeEntry[]
16
- }>
17
-
18
- export type KnowledgeEntry = Readonly<{
19
- id: string
20
- name: string
21
- description: string
22
- content: string
23
- tags: readonly string[]
24
- }>
25
-
26
- export const KnowledgeEntrySchema = z.object({
27
- id: z.string(),
28
- name: z.string(),
29
- description: z.string(),
30
- content: z.string(),
31
- tags: z.array(z.string()),
32
- })
33
-
34
- export type KnowledgeFeatures = Readonly<{
35
- getKnowledgeEntries: NilAnnotatedFunction<
36
- GetKnowledgeProps,
37
- GetKnowledgeOutput
38
- >
39
- }>
40
-
41
- export type KnowledgeFeaturesLayer = Readonly<{
42
- knowledge: KnowledgeFeatures
43
- }>
package/src/mcp/index.ts DELETED
@@ -1,4 +0,0 @@
1
- const name = 'mcp'
2
-
3
- export * as mcp from './mcp.js'
4
- export { name }
package/src/mcp/mcp.ts DELETED
@@ -1,56 +0,0 @@
1
- import { ServerTool } from '@l4t/mcp-ai/simple-server/types.js'
2
- import { createSimpleServer } from '@l4t/mcp-ai/simple-server/index.js'
3
- import {
4
- createMcpResponse,
5
- McpNamespace,
6
- nilAnnotatedFunctionToOpenApi,
7
- } from '@node-in-layers/mcp-server'
8
- import { NilAnnotatedFunction } from '@node-in-layers/core'
9
- import { JsonObj } from 'functional-models'
10
- import { McpMcp } from './types.js'
11
-
12
- export const create = (): McpMcp => {
13
- const tools: ServerTool[] = []
14
-
15
- const addFeature = <TProps extends JsonObj, TOutput extends JsonObj>(
16
- feature: NilAnnotatedFunction<TProps, TOutput>
17
- ) => {
18
- // @ts-ignore
19
- const openapi = nilAnnotatedFunctionToOpenApi(feature.name, feature)
20
- const tool: ServerTool = {
21
- name: feature.functionName,
22
- description: openapi.description || 'MCP function',
23
- inputSchema: openapi.input as any,
24
- outputSchema: openapi.output as any,
25
- execute: async (input: any) => {
26
- const r = await feature(input)
27
- // @ts-ignore
28
- if (!r) {
29
- return createMcpResponse(null)
30
- }
31
-
32
- return createMcpResponse(r)
33
- },
34
- }
35
- tools.push(tool)
36
- }
37
-
38
- const start = async () => {
39
- const server = createSimpleServer({
40
- name: '@node-in-layers/core/knowledge-mcp',
41
- version: '1.0.0',
42
- tools,
43
- server: {
44
- connection: {
45
- type: 'cli',
46
- },
47
- },
48
- })
49
- await server.start()
50
- }
51
-
52
- return {
53
- addFeature,
54
- start,
55
- }
56
- }
package/src/mcp/types.ts DELETED
@@ -1,25 +0,0 @@
1
- import { NilAnnotatedFunction } from '@node-in-layers/core'
2
- import { JsonObj } from 'functional-models'
3
-
4
- export type McpServices = Readonly<object>
5
-
6
- export type McpServicesLayer = Readonly<{
7
- mcp: McpServices
8
- }>
9
-
10
- export type McpFeatures = Readonly<object>
11
-
12
- export type McpFeaturesLayer = Readonly<{
13
- mcp: McpFeatures
14
- }>
15
-
16
- export type McpMcp = Readonly<{
17
- addFeature: <TProps extends JsonObj, TOutput extends JsonObj>(
18
- feature: NilAnnotatedFunction<TProps, TOutput>
19
- ) => void
20
- start: () => Promise<void>
21
- }>
22
-
23
- export type McpMcpLayer = Readonly<{
24
- mcp: McpMcp
25
- }>
package/src/types.ts DELETED
@@ -1,9 +0,0 @@
1
- import { KnowledgeFeatures, KnowledgeServices } from './knowledge/types'
2
- import { McpServerMcpLayer } from '@node-in-layers/mcp-server'
3
- import { McpMcpLayer } from './mcp/types.js'
4
-
5
- export type System = Readonly<{
6
- services: KnowledgeServices
7
- features: KnowledgeFeatures
8
- mcp: McpServerMcpLayer & McpMcpLayer
9
- }>
package/tsconfig.json DELETED
@@ -1,27 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "target": "es2022",
4
- "lib": ["es2021", "dom", "es2015"],
5
- "module": "ESNext",
6
- "moduleResolution": "bundler",
7
- "esModuleInterop": true,
8
- "rootDir": "./src",
9
- "allowJs": true,
10
- "declaration": true,
11
- "sourceMap": true,
12
- "outDir": "./dist",
13
- "forceConsistentCasingInFileNames": true,
14
- "strict": true,
15
- "noImplicitAny": false,
16
- "skipLibCheck": true
17
- },
18
- "include": ["./src"],
19
- "exclude": [
20
- "src/index.d.ts",
21
- "node_modules",
22
- "dist",
23
- "features",
24
- "container",
25
- "test"
26
- ]
27
- }
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes