lemon-core 5.0.4 → 5.0.6

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 (60) hide show
  1. package/README.md +168 -136
  2. package/bin/lemon-fields-types.mjs +12 -0
  3. package/bin/lib/fields-types.mjs +629 -0
  4. package/dist/common/test-helper.js +6 -3
  5. package/dist/common/test-helper.js.map +1 -1
  6. package/dist/cores/aws/aws-sns-service.d.ts +6 -0
  7. package/dist/cores/aws/aws-sns-service.js +22 -3
  8. package/dist/cores/aws/aws-sns-service.js.map +1 -1
  9. package/dist/cores/aws/aws-sqs-service.js +5 -0
  10. package/dist/cores/aws/aws-sqs-service.js.map +1 -1
  11. package/dist/cores/config/config-service.js +3 -1
  12. package/dist/cores/config/config-service.js.map +1 -1
  13. package/dist/cores/lambda/lambda-alb-handler.js +5 -0
  14. package/dist/cores/lambda/lambda-alb-handler.js.map +1 -1
  15. package/dist/cores/lambda/lambda-handler.js +4 -1
  16. package/dist/cores/lambda/lambda-handler.js.map +1 -1
  17. package/dist/cores/lambda/lambda-notification-handler.js +4 -1
  18. package/dist/cores/lambda/lambda-notification-handler.js.map +1 -1
  19. package/dist/cores/lambda/lambda-sns-handler.d.ts +7 -1
  20. package/dist/cores/lambda/lambda-sns-handler.js +26 -3
  21. package/dist/cores/lambda/lambda-sns-handler.js.map +1 -1
  22. package/dist/cores/lambda/lambda-sqs-handler.d.ts +7 -1
  23. package/dist/cores/lambda/lambda-sqs-handler.js +26 -3
  24. package/dist/cores/lambda/lambda-sqs-handler.js.map +1 -1
  25. package/dist/cores/lambda/lambda-web-handler.js +63 -15
  26. package/dist/cores/lambda/lambda-web-handler.js.map +1 -1
  27. package/dist/cores/protocol/aws-lambda-helper.d.ts +13 -0
  28. package/dist/cores/protocol/aws-lambda-helper.js +64 -41
  29. package/dist/cores/protocol/aws-lambda-helper.js.map +1 -1
  30. package/dist/cores/protocol/local-transport.d.ts +135 -0
  31. package/dist/cores/protocol/local-transport.js +392 -0
  32. package/dist/cores/protocol/local-transport.js.map +1 -0
  33. package/dist/cores/protocol/protocol-relay.d.ts +123 -0
  34. package/dist/cores/protocol/protocol-relay.js +339 -0
  35. package/dist/cores/protocol/protocol-relay.js.map +1 -0
  36. package/dist/cores/protocol/protocol-service.d.ts +11 -4
  37. package/dist/cores/protocol/protocol-service.js +140 -36
  38. package/dist/cores/protocol/protocol-service.js.map +1 -1
  39. package/dist/cores/protocol/protocol-sign.d.ts +22 -0
  40. package/dist/cores/protocol/protocol-sign.js +30 -3
  41. package/dist/cores/protocol/protocol-sign.js.map +1 -1
  42. package/dist/engine/engine.js +35 -15
  43. package/dist/engine/engine.js.map +1 -1
  44. package/dist/esm/index.mjs +2 -0
  45. package/dist/extended/cores/abstract-controllers.d.ts +18 -4
  46. package/dist/extended/cores/abstract-controllers.js +59 -19
  47. package/dist/extended/cores/abstract-controllers.js.map +1 -1
  48. package/dist/extended/cores/abstract-services.d.ts +54 -4
  49. package/dist/extended/cores/abstract-services.js +159 -14
  50. package/dist/extended/cores/abstract-services.js.map +1 -1
  51. package/dist/index.d.ts +3 -0
  52. package/dist/index.js +4 -1
  53. package/dist/index.js.map +1 -1
  54. package/dist/lib/log-mask.d.ts +19 -0
  55. package/dist/lib/log-mask.js +55 -0
  56. package/dist/lib/log-mask.js.map +1 -0
  57. package/dist/lib/safe-json.d.ts +23 -0
  58. package/dist/lib/safe-json.js +78 -0
  59. package/dist/lib/safe-json.js.map +1 -0
  60. package/package.json +14 -4
package/README.md CHANGED
@@ -2,47 +2,69 @@
2
2
  [![npm version](https://badge.fury.io/js/lemon-core.svg)](https://badge.fury.io/js/lemon-core)
3
3
  [![GitHub version](https://badge.fury.io/gh/lemoncloud-io%2Flemon-core.svg)](https://badge.fury.io/gh/lemoncloud-io%2Flemon-core)
4
4
 
5
- # lemon-core / V4
6
-
7
- Lemon Core Bootloader for Serverless Micro-Service
8
-
9
- - Support `multiple` event sources with single lambda function as below figure.
10
- - Fully support `typescript` types (80%).
11
- - Support Data Synchronization to `Elasticsearch` from `DynomoDB` via `DynamoStream`.
12
- - The way to migrate v3 to v4: SEE [HOW_TO_UPGRADE_V4](HOW_TO_UPGRADE_V4.md)
13
-
14
- ![](assets/2019-11-26-23-43-47.png)
15
-
16
- ## Architecture
17
-
18
- Basic MicroService Architecutre with `API` + `SNS` + `SQS`.
19
-
20
- - `NextHandler`: basic controller method to handle user service
21
- - `NextDecoder`: mapper from `httpMethod + id + cmd` to `NextHandler`
22
- - `NextContext`: initial requester's context with `identity`.
23
-
24
- ![](assets/lemon-core-ms-arch.png)
25
-
26
- ### Protocol Service
27
-
28
- - support inter-communication between micro services
29
- - `execute()`: synchronized call via lambda execution by `API` Handler.
30
- - `notifiy()`: async call by `SNS` handler w/ lambda callback.
31
- - `enqueue()`: async call by `SQS` handler w/ lambda callback.
32
- - `broadcast()`: publish message via `SNS`, and handled by `Notification` handler.
33
-
34
- ![](assets/lemon-protocol-flow.png)
35
-
36
- ```ts
37
- import $engine, { ProtocolParam, ProtocolService, CallbackParam } from 'lemon-core';
38
- // use the internal instance from $engine.
39
- const service: ProtocolService = $engine.cores.protocol.service;
40
- const protocol: ProtocolParam = service.fromURL(context, 'api://lemon-hello-api/hello/echo', param, body);
41
- const callback: CallbackParam = { type: 'hooks', id: `${id}` };
42
- // queue protocol in 30 seconds delayed.
43
- const queueId = await service.enqueue(protocol, callback, 30);
5
+ # lemon-core
6
+
7
+ Bootloader for serverless micro-services on AWS Lambda: one function, several event sources, an engine with modules, a protocol service.
8
+ This README is the entry point for people and AI agents; every path and command below is in this repo. `§` refers to `HOW_TO_UPGRADE_V5.md`.
9
+ v5 status: npm `latest` is `5.0.4`. The `v5` branch holds all 5.x work, unreleased changes included; `develop` is the default branch.
10
+ `CONTRIBUTING.md` describes the `develop` flow; 5.x changes are merged into `v5` until it lands on `develop`.
11
+
12
+ ## At a glance
13
+
14
+ | Item | Value |
15
+ |---|---|
16
+ | Runtime | Node `>=24.0.0` (`engines.node`), `.nvmrc` `24.15.0` |
17
+ | Build | TypeScript `^5.9.3`. `npm run build` = `build-ts` (`tsc`) + `scripts/gen-esm-wrapper.cjs` |
18
+ | Tests | vitest `^4.1.4`: `src/**/*.spec.ts`, `bin/**/*.spec.mjs` (`vitest.config.ts`) |
19
+ | Formats | CJS `dist/` + ESM `dist/esm/*.mjs`. `exports`: `.`, `./testing`, `./dist/environ`, `./dist/cores/lambda/lambda-web-handler`, `./dist/extended/cores`, `./dist/*` (until v6), `./package.json` |
20
+ | Dependencies | `@aws-sdk/client-dynamodb, @aws-sdk/lib-dynamodb, @opensearch-project/opensearch, @smithy/signature-v4, @types/aws-lambda, @types/jsonwebtoken, async, crypto-js, js-yaml, jsonwebtoken, lemon-model, mime-types, node-cache, qs, source-map-support` |
21
+ | Optional peers | 8 AWS SDK packages `^3.1159.0`, loaded on first use. `typescript` `>=4.7` (CLI) |
22
+ | Published | `files`: `dist/**/*`, `bin/lemon-fields-types.mjs`, `bin/lib/fields-types.mjs`. CLI `lemon-fields-types` (merged in the `v5` branch after 5.0.4; see § 13.5) |
23
+ | Branches | `develop` is the default branch (its `version` is 4.3.0). `v5` holds the 5.x work. `master` was last updated in 2024 (3.2.9). |
24
+ | Version | The `version` field of `package.json`. |
25
+
26
+ ## Repository map
27
+
28
+ | Path | What it holds | Who changes it |
29
+ |---|---|---|
30
+ | `src/index.ts` | Root barrel | Only through the `api:check` allow list |
31
+ | `src/engine/` | `$engine` singleton, `$U`, `_log`/`_inf`/`_err` | Rarely; every module and service depends on it |
32
+ | `src/cores/` | `lambda/`, `protocol/`, `config/`, `aws/`, `dynamo/`, `elastic/`, `storage/`, `cache/`, `api/` | Developer |
33
+ | `src/extended/` | `CoreService`, `ManagerProxy`; `cores/`: template layer (`MyCoreService`, `checkFieldsAdmin()`) | Developer |
34
+ | `src/controllers/`, `tools/`, `helpers/` | Controllers, `express`, `$T`, `$protocol()` | Developer |
35
+ | `src/lib/` | Internal: `safe-json`, `log-mask`, `qs-compat`, `http-fetch`, `lazy`, `perf`. `lib` barrel: `dynamodb-value` only | Developer. Internal; never re-exported from a barrel |
36
+ | `src/common/test-helper.ts`, `src/environ.ts` | Sub-paths `lemon-core/testing`, `lemon-core/dist/environ` | Rarely; both are published sub-paths |
37
+ | `bin/` | `lemon-fields-types` CLI, `lib/` logic, spec, fixtures | Developer |
38
+ | `scripts/` | Build and API gates, lazy-load probes | Developer |
39
+ | `etc/api-names.v4.txt`, `etc/api-names.allow.txt` | v4 public-name baseline; allowed `+Name`/`-Name` | The baseline stays; add lines to the allow list only |
40
+ | `env/` | Configuration profiles | **Agents do not open it** |
41
+ | `docs/v5/`, `docs/specs/` | v5 design record (B0–B13); `$T`/`$U` specs | Updated with the change they describe |
42
+ | `dist/`, `coverage/` | Build and test output, ignored by git | **Never edited by hand** |
43
+ | `.github/workflows/` | `docgen.yaml`: typedoc on push to `develop`. No test job | A person |
44
+
45
+ ## Request flow
46
+
47
+ ```mermaid
48
+ flowchart LR
49
+ SRC["API Gateway, ALB, WebSocket, SNS, SQS,<br/>cron, DynamoDB stream, Cognito"] --> LH["LambdaHandler<br/>findService()"]
50
+ LH -- web --> WEB["LambdaWEBHandler"]
51
+ LH -- "sns / sqs" --> MQ["LambdaSNSHandler, LambdaSQSHandler"]
52
+ LH -- "alb, cron, dds, ..." --> SUB["other handlers"]
53
+ MQ -- "x-protocol-service" --> WEB
54
+ MQ -- other --> LIS["addListener()"]
55
+ WEB --> CTRL["NextDecoder → NextHandler"]
56
+ CTRL --> PS["ProtocolService"]
57
+ PS -- "execute(): Lambda invoke" --> OUT["other service"]
58
+ PS -- "notify(), broadcast(): SNS" --> OUT
59
+ PS -- "enqueue(): SQS" --> OUT
44
60
  ```
45
61
 
62
+ - `LambdaModule` (`cores/lambda/`) runs `$engine.initialize()`, then `LambdaHandler.handle()`.
63
+ - Web: the type's controller (`addController()`) decodes to a `NextHandler` with a `NextContext`.
64
+ - SNS/SQS with `Subject` `x-protocol-service` pass a signature check, then `handleProtocol()`.
65
+ - `ProtocolService` (`core-services.ts`): `execute()` waits (unless `useEvent`); `notify()`, `enqueue()`, `broadcast()` do not.
66
+ - Failures resolve (Cognito rethrows); SQS/stream return `batchItemFailures` (§ 11.4).
67
+
46
68
  ## Usage
47
69
 
48
70
  ### 1. Install
@@ -51,132 +73,136 @@ const queueId = await service.enqueue(protocol, callback, 30);
51
73
  npm install lemon-core --save
52
74
  ```
53
75
 
54
- Requires Node `>=24.0.0` (`package.json#engines.node`).
76
+ Add the AWS SDK peers you use to your own `dependencies` (§ 8.1).
55
77
 
56
78
  ### 2. Minimal bootstrap — engine initialization
57
79
 
58
- Importing `lemon-core` creates the `$engine` singleton immediately at module
59
- load time (not lazily):
60
-
61
80
  ```ts
62
- // src/engine/index.ts:23
63
- // export const $engine: LemonEngine = buildEngine(global, { env: process.env });
64
- // src/engine/index.ts:26,30-32
65
- // export const $U = $engine.U;
66
- // export const _log = $engine.log; export const _inf = $engine.inf; export const _err = $engine.err;
67
- import { $U, _log, _inf, _err } from 'lemon-core';
68
-
81
+ // `$engine` is built at the first import, not lazily: set `process.env` before.
82
+ import $lemon, { $U, _inf } from 'lemon-core'; // default: { engine, cores, tools, controllers }
69
83
  _inf('lemon-core loaded, NS=', $U.env('NS', 'TT'));
84
+ await $lemon.engine.initialize(); // resolves once every module's initModule() settles
70
85
  ```
71
86
 
72
- The root package also exposes a default export that bundles the four
73
- top-level groups (`engine`, `cores`, `tools`, `controllers`):
74
-
75
- ```ts
76
- // src/index.ts:68 — export default { engine, cores, tools, controllers };
77
- import $lemon from 'lemon-core';
78
-
79
- await $lemon.engine.initialize(); // resolves once every registered module's initModule() settles
80
- ```
87
+ ESM `import` gives the same instances (§ 2). Specs use `lemon-core/testing` (§ 4).
81
88
 
82
89
  ### 3. Engine module registration — the "3-piece set"
83
90
 
84
- Any module that hangs off `$engine` (built-in or your own) follows the same
85
- contract: register in the constructor, name itself, expose an init level.
86
- The built-in `ProtocolModule` is the smallest real example:
87
-
88
91
  ```ts
89
- // src/cores/protocol/index.ts:16-34
90
- // export class ProtocolModule implements EngineModule {
91
- // public constructor(engine?: LemonEngine) {
92
- // this.engine = engine || $engine;
93
- // if (this.engine) this.engine.register(this); // (1) register
94
- // }
95
- // public readonly service: ProtocolService = new MyProtocolService();
96
- // public getModuleName = () => 'protocol'; // (2) name
97
- // public async initModule(level?: number): Promise<number> { // (3) init level
98
- // const $conf = this.engine.module<ConfigModule>('config');
99
- // if (level === undefined) return $conf ? (await $conf.initModule()) + 1 : 1;
100
- // }
101
- // }
102
- // export default new ProtocolModule(); // <- import-time instance, not lazy
92
+ // src/cores/protocol/index.ts (excerpt)
93
+ export class ProtocolModule implements EngineModule {
94
+ public constructor(engine?: LemonEngine) {
95
+ this.engine = engine || $engine;
96
+ if (this.engine) this.engine.register(this); // (1) register; a same-name module is ignored
97
+ }
98
+ public getModuleName = () => 'protocol'; // (2) name
99
+ public async initModule(level?: number): Promise<number> {
100
+ const $conf = this.engine.module<ConfigModule>('config'); // (3) init level
101
+ if (level === undefined) return $conf ? (await $conf.initModule()) + 1 : 1;
102
+ }
103
+ public readonly service: ProtocolService = new MyProtocolService();
104
+ }
105
+ export default new ProtocolModule(); // import-time instance, not lazy (`cores` group)
103
106
  ```
104
107
 
105
- `cores/index.ts:33` re-exports it as part of the `cores` default group
106
- (`export default { aws, config, lambda, protocol }`), which is why the
107
- existing `$engine.cores.protocol.service` example below resolves.
108
-
109
108
  ### 4. Manager / proxy registration — inter-service call example
110
109
 
111
- `ProtocolService` (registered above) is the mechanism for calling another
112
- micro-service — synchronously via `execute()`, or asynchronously via
113
- `notify()` / `enqueue()` / `broadcast()`:
114
-
115
110
  ```ts
116
- // import path verified: root barrel re-exports src/cores/core-services.ts
117
- // (ProtocolParam: :99, CallbackParam: :88, ProtocolService: :150)
118
- import $engine, { ProtocolParam, ProtocolService, CallbackParam } from 'lemon-core';
111
+ import $lemon, { CallbackParam, ProtocolParam, ProtocolService } from 'lemon-core';
119
112
 
120
- const service: ProtocolService = $engine.cores.protocol.service;
113
+ const service: ProtocolService = $lemon.cores.protocol.service;
121
114
  const protocol: ProtocolParam = service.fromURL(context, 'api://lemon-hello-api/hello/echo', param, body);
122
115
  const callback: CallbackParam = { type: 'hooks', id: `${id}` };
123
- // queue protocol in 30 seconds delayed.
124
- const queueId = await service.enqueue(protocol, callback, 30);
116
+ const queueId = await service.enqueue(protocol, callback, 30); // 30 seconds delayed
125
117
  ```
126
118
 
127
- For model persistence, lemon-core's storage/model layer (see the class table
128
- below) is meant to be **subclassed**, not used directly — `AbstractManager`
129
- and `MyCoreService` are abstract:
119
+ Controllers: `$lemon.cores.lambda.web.addController()`. Models: subclass `MyCoreService` (abstract `createProxy()`), `MyCoreManager`, `MyCoreProxy`. v5: `guardProxy()` saves on a throw only with `{ saveOnError: true }`; managers sync to OpenSearch only with `{ enableSync: true }` (§ 11.6).
130
120
 
131
- ```ts
132
- // class contracts (all exported from the root barrel):
133
- // - CoreService src/extended/abstract-service.ts:115 (table/ns/idName, makeStorageService())
134
- // - AbstractManager src/cores/storage/model-manager.ts:23 (storage owner; must implement
135
- // `protected abstract prepareDefault($def: T): T` — model-manager.ts:66)
136
- // - MyCoreService src/extended/cores/abstract-services.ts:171
137
- // (adds `guardProxy(context, cb)` — auto-saves via `proxy.saveAllUpdates()`
138
- // on both success and throw — abstract-services.ts:189-202)
139
- // - MyCoreProxy src/extended/cores/abstract-services.ts:912
140
- // - ManagerProxy src/extended/abstract-service.ts:399 (auto-registers via `proxy.register(this)`
141
- // in its own constructor — abstract-service.ts:406)
142
- // - AbstractProxy src/extended/abstract-service.ts:652 (container; `saveAllUpdates()` persists
143
- // every registered ManagerProxy's changes in one call)
144
- // - NextContext re-exported from the `lemon-model` package via
145
- // `export * from 'lemon-model';` — src/cores/index.ts:12
146
-
147
- class MyService extends MyCoreService<MyModel, 'my-type', MyProxy> {
148
- createProxy(context: NextContext): MyProxy {
149
- return new MyProxy(context, this);
150
- }
151
- }
121
+ ## Commands
152
122
 
153
- // typical call site:
154
- const result = await service.guardProxy(context, async proxy => {
155
- // read/modify via proxy.<manager>.get()/.set() ...
156
- return something;
157
- }); // saveAllUpdates() runs automatically, even if the callback throws
158
- ```
123
+ | Command | What it does | Note |
124
+ |---|---|---|
125
+ | `npm test` | `LS=1 vitest run` | Writes `coverage/`. |
126
+ | `npm run test:watch` | `LS=1 vitest` | Re-runs on file changes. |
127
+ | `npm run build` | `tsc` + ESM wrapper | Writes `dist/`. |
128
+ | `npx tsc --noEmit` | Type check only | Writes nothing. |
129
+ | `npm run lint` | `tsc --noEmit` + `eslint --fix` | **Rewrites files.** |
130
+ | `npm run format` | `prettier --write` on `src/` | **Rewrites files.** |
131
+ | `npm run api:names` | Root public names of `dist/index.d.ts` | Run `npm run build` first. |
132
+ | `npm run api:check` | Names vs `etc/api-names.v4.txt` + allow list | Run after `build`. Expect `not-allowed=0`; otherwise exit 1. |
133
+ | `npm run esm:smoke` | ESM and CJS entries are one instance | Run after `build`. Expect `esm-smoke: ok …`. |
134
+ | `npm run dup:check` | One declaring file per exported name | Expect `not-allowed=0`. |
135
+ | `npm run l1l2:probe` | Root/L1/L2 identity, deep paths (`dist/`) | Run after `build`. Expect `fails=0`. |
136
+ | `npm run fields:types` | `lemon-fields-types` here | Reports `models=0` in this repo. Consumers run `npx lemon-fields-types --root .`. |
137
+ | `npm audit --omit=dev` | Audit runtime dependencies | Expect `found 0 vulnerabilities`. |
159
138
 
160
- This is a skeleton, not a runnable app — see the reference links below for a
161
- worked model + manager + proxy example.
139
+ ## Verifying a change
162
140
 
163
- ### 5. Run tests
141
+ Before saying a change is done, run:
164
142
 
165
143
  ```sh
166
- LS=1 vitest run
144
+ npm test # all test files pass, exit 0
145
+ npm run build # exit 0
146
+ npx tsc --noEmit # no output
147
+ npm run api:check # api-check: … not-allowed=0
148
+ npm run esm:smoke # esm-smoke: ok …
149
+ npm audit --omit=dev # found 0 vulnerabilities
167
150
  ```
168
151
 
169
- (exact value of `package.json#scripts.test`, 2026-09-02)
170
-
171
- ### 6. Reference
172
-
173
- - Full `ProtocolService` surface: `src/cores/core-services.ts`.
174
- - Storage/model layer: `src/extended/abstract-service.ts`,
175
- `src/extended/cores/abstract-services.ts`, `src/cores/storage/model-manager.ts`.
176
- - Generated API docs: *(typedoc output — link TBD, see B1 re-review doc,
177
- item 5 "API 문서 공개 전략"; current CI publishes to both an S3 bucket and
178
- `gh-pages` with no README link — not resolved in this draft)*.
179
- - `docs/header-identity-token.md` (existing hand-written doc in this repo).
152
+ - `api:check`: no root name changes without an allow-list line.
153
+ - `esm:smoke`: the dual package keeps one `$engine`.
154
+ - `npm audit --omit=dev`: runtime dependencies stay at 0 (§ 14).
155
+ - Moved exports: also `dup:check`, `l1l2:probe`.
156
+
157
+ ## Agent rules
158
+
159
+ - Public names change only via `api:check`: a `+Name`/`-Name` line in `etc/api-names.allow.txt`; the v4 baseline stays.
160
+ - `src/lib/*` is internal: never re-export it from `src/index.ts` or any barrel.
161
+ - No new runtime dependency unless `npm audit --omit=dev` stays 0 (§ 14).
162
+ - The 8 AWS SDK peers stay optional; SDK code loads on first use (§ 8).
163
+ - No `npm publish`, deploy or version bump: a person does those.
164
+ - 5.x work branches from `v5`.
165
+ - `lint` and `format` rewrite files: run them on purpose.
166
+ - Never open or print `env/*` or configuration values.
167
+ - When the docs and the source disagree, the source is right.
168
+ - New functions and blocks get a one-line comment (`src/lib/log-mask.ts`).
169
+
170
+ ## Document index
171
+
172
+ | Document | Content |
173
+ |---|---|
174
+ | [HOW_TO_UPGRADE_V5.md](HOW_TO_UPGRADE_V5.md) | § 1 Node 24 · § 2 ESM · § 3 `exports` · § 4 testing · § 5 modules · § 6 deps · § 7 OpenSearch · § 8 AWS SDK · § 9 L1/L2 · § 10 `@field()` · § 11 trust · § 12 `src/cores/` · § 13 B10 · § 14 audit |
175
+ | [HOW_TO_UPGRADE_V4.md](HOW_TO_UPGRADE_V4.md) | v3 → v4 (historical) |
176
+ | [baseline.md](docs/v5/baseline.md) | B0-a baseline: link state, boot, run modes, test counts, public names |
177
+ | [B0-local-harness.md](docs/v5/B0-local-harness.md) | B0 local harness: the local run path as the verification gate |
178
+ | [B1-packaging.md](docs/v5/B1-packaging.md) | B1 packaging: dual entry, `exports`, single instance |
179
+ | [B2-inventory.md](docs/v5/B2-inventory.md) | B2 (dependencies and coupling): measured inventory |
180
+ | [B2-a-deps.md](docs/v5/B2-a-deps.md) | B2-a: dependencies out: `request`, `lodash`, `ioredis`, `memcached` |
181
+ | [B2-b-inventory.md](docs/v5/B2-b-inventory.md) | B2-b (search): measured inventory |
182
+ | [B2-b-search.md](docs/v5/B2-b-search.md) | B2-b search: OpenSearch client, ES names out, lazy `$OS2` |
183
+ | [B2-c-inventory.md](docs/v5/B2-c-inventory.md) | B2-c (AWS SDK soft coupling): measured inventory |
184
+ | [B2-c-aws.md](docs/v5/B2-c-aws.md) | B2-c AWS SDK: optional peers, lazy loading, local types, `PERF` lines |
185
+ | [B3-inventory.md](docs/v5/B3-inventory.md) | B3 (L1/L2 integration): measured inventory |
186
+ | [B3-l1-l2.md](docs/v5/B3-l1-l2.md) | B3 L1/L2 merge: one definition per name, removed aliases, renamed files |
187
+ | [B4-inventory.md](docs/v5/B4-inventory.md) | B4 (field annotations): measured inventory and experiments |
188
+ | [B4-field-annotation.md](docs/v5/B4-field-annotation.md) | B4: `@field()` in lemon-model, generated registry removed |
189
+ | [B5-inventory.md](docs/v5/B5-inventory.md) | B5 inventory: trust boundary and behaviour contract |
190
+ | [B5-trust-boundary.md](docs/v5/B5-trust-boundary.md) | B5: trust boundary and behaviour contract |
191
+ | [B6-dou-migration.md](docs/v5/B6-dou-migration.md) | B6: migrating a service that carries a `src/cores/` copy |
192
+ | [B7-perf-trace.md](docs/v5/B7-perf-trace.md) | B7: per-request `PERF` lines (`handle`, `request` + 4 steps) |
193
+ | [B8-kms-fetch.md](docs/v5/B8-kms-fetch.md) | B8: KMS `Decrypt` without the SDK (SigV4 + Node `https`) |
194
+ | [B10-context-source.md](docs/v5/B10-context-source.md) | B10: the first request path in `context.source` (Korean) |
195
+ | [B10-fields-types.md](docs/v5/B10-fields-types.md) | B10: field name/type consistency check `lemon-fields-types` (Korean) |
196
+ | [B10-header-log-mask.md](docs/v5/B10-header-log-mask.md) | B10: masking sensitive headers in logs (Korean) |
197
+ | [B10-report-account.md](docs/v5/B10-report-account.md) | B10: choosing the SNS account number in `doReportError()` (Korean) |
198
+ | [B10-report-circular.md](docs/v5/B10-report-circular.md) | B10: circular structures in error reports (Korean) |
199
+ | [B10-verify-enforce.md](docs/v5/B10-verify-enforce.md) | B10: runbook for switching to `PROTOCOL_VERIFY=enforce` (Korean) |
200
+ | [B11-deps-audit.md](docs/v5/B11-deps-audit.md) | B11: `npm audit --omit=dev` 0, `query-string` → `qs`, `uuid` removed (Korean) |
201
+ | [B13-access-policy.md](docs/v5/B13-access-policy.md) | B13: default data-access policy of `AbstractCRUDController` (`AccessPolicy`, `canDo` scopes) |
202
+ | [docs/specs/](docs/specs/) | `$T`, `$U` characterization specs |
203
+ | [docs/header-identity-token.md](docs/header-identity-token.md) | Identity token header |
204
+ | [CONTRIBUTING](CONTRIBUTING.md), [SECURITY](SECURITY.md), [CODE_OF_CONDUCT](CODE_OF_CONDUCT.md) | PR process; supported versions; conduct |
205
+ | [CHANGELOG.md](CHANGELOG.md) | Unreleased 5.x entry (B13), then stops at 1.2.0 (2019); see VERSION INFO |
180
206
 
181
207
  ## Contribution
182
208
 
@@ -194,6 +220,12 @@ See [CODE_OF_CONDUCT](CODE_OF_CONDUCT.md)
194
220
 
195
221
  | Version | Description
196
222
  |-- |--
223
+ | unreleased (v5) | B10: `getHelloArn()` account (§ 13.1), circular-safe reports (§ 13.2), masked header logs (§ 13.3), enforce runbook + masked config log (§ 13.4), `lemon-fields-types` (§ 13.5); `LOCAL_TRANSPORT=mock` opt-in (§ 11.3.1).
224
+ | 5.0.4 | `context.source` carries the first request path; `npm audit --omit=dev` 0 (`qs`, `randomUUID()`, AWS SDK `^3.1159.0`).
225
+ | 5.0.3 | KMS `Decrypt` via SigV4 + Node `https`, without `@aws-sdk/client-kms` on the boot path.
226
+ | 5.0.2 | per-request `PERF` lines (`request`, `handle`).
227
+ | 5.0.1 | `GET /<type>/fields/admin` + `checkFieldsAdmin()`; env `DEFAULT_TIME_ZONE`, `DATE_ONLY_CLOCK`, `SID_LEGACY_ACCESS`.
228
+ | 5.0.0 | Node 24, ESM entry + `exports`, AWS SDK optional peers, OpenSearch client, `@field()`, signed protocol calls.
197
229
  | 4.3.0 | embed cores template layer as extended/cores
198
230
  | 4.2.6 | optimized `$protocol.execute()` to support async lambda invocation.
199
231
  | 4.2.5 | optimized `isBase64Encoded` to support apigwBinary. (from `4.1.17`)
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * `bin/lemon-fields-types.mjs` (lemon-core)
4
+ * - check that one field name has one type across the stored models of one OpenSearch index (`$FIELD` models).
5
+ * - usage: `lemon-fields-types [options]` (installed bin) / `npx lemon-fields-types` / `npm run fields:types -- [options]`.
6
+ * - all logic is in `lib/fields-types.mjs`; this file only sets the exit code.
7
+ */
8
+ //* v5 B10-5: the CLI entry of the shared field-type checker (ported from lemon-templates-api `bin/fields-types.mjs`).
9
+ import { run } from './lib/fields-types.mjs';
10
+
11
+ // run on the real file system; `run` never throws and returns the exit code.
12
+ process.exitCode = run(process.argv.slice(2));