@sincpro/client-framework 0.6.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (176) hide show
  1. package/AGENTS.md +95 -25
  2. package/MANIFESTO.md +103 -40
  3. package/README.md +608 -333
  4. package/dist/circuit_breaker/domain/circuit.d.ts +29 -0
  5. package/dist/circuit_breaker/domain/circuit.js +37 -0
  6. package/dist/circuit_breaker/entrypoint/circuit-breaker.d.ts +56 -0
  7. package/dist/circuit_breaker/entrypoint/circuit-breaker.js +94 -0
  8. package/dist/circuit_breaker/index.d.ts +9 -0
  9. package/dist/circuit_breaker/index.js +9 -0
  10. package/dist/client/adapters/json-rpc.d.ts +40 -0
  11. package/dist/client/adapters/json-rpc.js +97 -0
  12. package/dist/client/{memory.d.ts → adapters/memory.d.ts} +1 -1
  13. package/dist/client/{memory.js → adapters/memory.js} +2 -2
  14. package/dist/{adapters → client/adapters}/parquet.d.ts +1 -1
  15. package/dist/client/adapters/rest-operations.d.ts +19 -0
  16. package/dist/client/adapters/rest-operations.js +66 -0
  17. package/dist/client/{rest.d.ts → adapters/rest.d.ts} +1 -1
  18. package/dist/client/{rest.js → adapters/rest.js} +2 -2
  19. package/dist/client/{entity.d.ts → domain/entity.d.ts} +6 -10
  20. package/dist/client/domain/operation.d.ts +73 -0
  21. package/dist/client/domain/operation.js +19 -0
  22. package/dist/client/index.d.ts +20 -0
  23. package/dist/client/index.js +20 -0
  24. package/dist/client/services/guarded.d.ts +17 -0
  25. package/dist/client/services/guarded.js +19 -0
  26. package/dist/client/services/propagation.d.ts +45 -0
  27. package/dist/{services/execution.js → client/services/propagation.js} +49 -36
  28. package/dist/client/{retry.js → services/retry.js} +1 -1
  29. package/dist/codegen/{metamodel.d.ts → domain/metamodel.d.ts} +17 -2
  30. package/dist/codegen/{names.d.ts → domain/names.d.ts} +2 -2
  31. package/dist/codegen/{names.js → domain/names.js} +3 -3
  32. package/dist/codegen/{cli.js → entrypoint/cli.js} +15 -14
  33. package/dist/codegen/index.d.ts +7 -7
  34. package/dist/codegen/index.js +5 -10
  35. package/dist/codegen/services/client.d.ts +15 -0
  36. package/dist/codegen/services/client.js +111 -0
  37. package/dist/codegen/services/domain.d.ts +20 -0
  38. package/dist/codegen/{domain.js → services/domain.js} +82 -25
  39. package/dist/codegen/{entities.d.ts → services/entities.d.ts} +4 -3
  40. package/dist/codegen/{entities.js → services/entities.js} +18 -4
  41. package/dist/codegen/{rest.d.ts → services/rest.d.ts} +1 -1
  42. package/dist/codegen/{scaffold.d.ts → services/scaffold.d.ts} +8 -5
  43. package/dist/codegen/{scaffold.js → services/scaffold.js} +39 -43
  44. package/dist/common/failures.d.ts +83 -0
  45. package/dist/common/failures.js +212 -0
  46. package/dist/common/index.d.ts +11 -0
  47. package/dist/common/index.js +11 -0
  48. package/dist/context/adapters/memory.d.ts +6 -0
  49. package/dist/context/adapters/memory.js +34 -0
  50. package/dist/context/adapters/plain-codec.d.ts +3 -0
  51. package/dist/context/adapters/plain-codec.js +16 -0
  52. package/dist/context/{codec.d.ts → domain/codec.d.ts} +0 -1
  53. package/dist/context/{codec.js → domain/codec.js} +0 -14
  54. package/dist/context/domain/execution.d.ts +45 -0
  55. package/dist/context/domain/execution.js +11 -0
  56. package/dist/context/{store.d.ts → domain/store.d.ts} +0 -5
  57. package/dist/context/{store.js → domain/store.js} +1 -34
  58. package/dist/context/{context.d.ts → entrypoint/context.d.ts} +1 -1
  59. package/dist/context/index.d.ts +17 -0
  60. package/dist/context/index.js +15 -0
  61. package/dist/context/infrastructure/process.d.ts +13 -0
  62. package/dist/context/infrastructure/process.js +35 -0
  63. package/dist/core/domain/bus.d.ts +27 -0
  64. package/dist/core/domain/bus.js +26 -0
  65. package/dist/core/domain/dto.d.ts +62 -0
  66. package/dist/core/domain/dto.js +43 -0
  67. package/dist/core/domain/errors.d.ts +18 -0
  68. package/dist/core/domain/errors.js +1 -0
  69. package/dist/core/domain/pipeline.d.ts +89 -0
  70. package/dist/core/domain/pipeline.js +1 -0
  71. package/dist/core/domain/services.d.ts +47 -0
  72. package/dist/core/domain/services.js +31 -0
  73. package/dist/core/entrypoint/bounded-context.d.ts +139 -0
  74. package/dist/core/entrypoint/bounded-context.js +426 -0
  75. package/dist/core/entrypoint/screens.d.ts +6 -0
  76. package/dist/core/entrypoint/screens.js +136 -0
  77. package/dist/core/index.d.ts +19 -0
  78. package/dist/core/index.js +17 -0
  79. package/dist/core/infrastructure/listeners.d.ts +28 -0
  80. package/dist/core/infrastructure/listeners.js +34 -0
  81. package/dist/core/infrastructure/process.d.ts +56 -0
  82. package/dist/core/infrastructure/process.js +35 -0
  83. package/dist/core/services/binding.d.ts +6 -0
  84. package/dist/core/services/binding.js +16 -0
  85. package/dist/core/services/error-handlers.d.ts +11 -0
  86. package/dist/core/services/error-handlers.js +21 -0
  87. package/dist/core/services/execution.d.ts +66 -0
  88. package/dist/core/services/execution.js +216 -0
  89. package/dist/core/services/interceptors.d.ts +15 -0
  90. package/dist/core/services/interceptors.js +25 -0
  91. package/dist/core/services/listening.d.ts +17 -0
  92. package/dist/core/services/listening.js +25 -0
  93. package/dist/event_driven/adapters/memory-event-store.d.ts +10 -0
  94. package/dist/event_driven/adapters/memory-event-store.js +23 -0
  95. package/dist/event_driven/adapters/stored-queue.d.ts +67 -0
  96. package/dist/event_driven/adapters/stored-queue.js +192 -0
  97. package/dist/event_driven/adapters/sync-queue.d.ts +11 -0
  98. package/dist/event_driven/adapters/sync-queue.js +19 -0
  99. package/dist/event_driven/domain/envelope.d.ts +28 -0
  100. package/dist/event_driven/domain/envelope.js +19 -0
  101. package/dist/event_driven/domain/queue.d.ts +40 -0
  102. package/dist/event_driven/domain/queue.js +1 -0
  103. package/dist/event_driven/domain/store.d.ts +56 -0
  104. package/dist/event_driven/domain/store.js +1 -0
  105. package/dist/event_driven/entrypoint/publisher.d.ts +26 -0
  106. package/dist/event_driven/entrypoint/publisher.js +35 -0
  107. package/dist/event_driven/entrypoint/subscriber.d.ts +34 -0
  108. package/dist/event_driven/entrypoint/subscriber.js +51 -0
  109. package/dist/event_driven/index.d.ts +19 -0
  110. package/dist/event_driven/index.js +17 -0
  111. package/dist/index.d.ts +21 -38
  112. package/dist/index.js +21 -38
  113. package/dist/model/index.d.ts +10 -0
  114. package/dist/model/index.js +10 -0
  115. package/dist/model/{metas.d.ts → infrastructure/metas.d.ts} +1 -1
  116. package/dist/model/{schema.d.ts → services/schema.d.ts} +1 -1
  117. package/dist/model/{schema.js → services/schema.js} +1 -1
  118. package/dist/observability/adapters/console.d.ts +12 -0
  119. package/dist/observability/adapters/console.js +35 -0
  120. package/dist/observability/adapters/memory.d.ts +41 -0
  121. package/dist/observability/adapters/memory.js +49 -0
  122. package/dist/{adapters → observability/adapters}/opentelemetry.d.ts +1 -1
  123. package/dist/{adapters → observability/adapters}/sentry.d.ts +1 -1
  124. package/dist/observability/domain/errors.d.ts +23 -0
  125. package/dist/observability/domain/errors.js +8 -0
  126. package/dist/observability/domain/logging.d.ts +44 -0
  127. package/dist/observability/domain/logging.js +1 -0
  128. package/dist/observability/domain/tracing.d.ts +49 -0
  129. package/dist/observability/domain/tracing.js +20 -0
  130. package/dist/observability/index.d.ts +16 -0
  131. package/dist/observability/index.js +4 -0
  132. package/dist/observability/infrastructure/registry.d.ts +21 -0
  133. package/dist/observability/infrastructure/registry.js +18 -0
  134. package/dist/observability/services/logging.d.ts +27 -0
  135. package/dist/observability/services/logging.js +106 -0
  136. package/dist/observability/services/outcome.d.ts +12 -0
  137. package/dist/observability/services/outcome.js +17 -0
  138. package/dist/{services/report.js → observability/services/reporting.js} +4 -3
  139. package/dist/testing/index.d.ts +3 -33
  140. package/dist/testing/index.js +5 -37
  141. package/package.json +16 -8
  142. package/dist/codegen/client.d.ts +0 -14
  143. package/dist/codegen/client.js +0 -201
  144. package/dist/codegen/domain.d.ts +0 -15
  145. package/dist/observability/ports.d.ts +0 -65
  146. package/dist/observability/ports.js +0 -21
  147. package/dist/services/bounded-context.d.ts +0 -79
  148. package/dist/services/bounded-context.js +0 -261
  149. package/dist/services/execution.d.ts +0 -63
  150. package/dist/services/feature.d.ts +0 -85
  151. package/dist/services/feature.js +0 -86
  152. package/dist/services/process.d.ts +0 -60
  153. package/dist/services/process.js +0 -57
  154. package/dist/services/screens.d.ts +0 -2
  155. package/dist/services/screens.js +0 -76
  156. /package/dist/{adapters → client/adapters}/parquet.js +0 -0
  157. /package/dist/client/{contract.d.ts → domain/contract.d.ts} +0 -0
  158. /package/dist/client/{contract.js → domain/contract.js} +0 -0
  159. /package/dist/client/{entity.js → domain/entity.js} +0 -0
  160. /package/dist/client/{transport.d.ts → domain/transport.d.ts} +0 -0
  161. /package/dist/client/{transport.js → domain/transport.js} +0 -0
  162. /package/dist/client/{discovery.d.ts → services/discovery.d.ts} +0 -0
  163. /package/dist/client/{discovery.js → services/discovery.js} +0 -0
  164. /package/dist/client/{retry.d.ts → services/retry.d.ts} +0 -0
  165. /package/dist/codegen/{metamodel.js → domain/metamodel.js} +0 -0
  166. /package/dist/codegen/{cli.d.ts → entrypoint/cli.d.ts} +0 -0
  167. /package/dist/codegen/{check.d.ts → services/check.d.ts} +0 -0
  168. /package/dist/codegen/{check.js → services/check.js} +0 -0
  169. /package/dist/codegen/{rest.js → services/rest.js} +0 -0
  170. /package/dist/context/{context.js → entrypoint/context.js} +0 -0
  171. /package/dist/model/{meta.d.ts → domain/meta.d.ts} +0 -0
  172. /package/dist/model/{meta.js → domain/meta.js} +0 -0
  173. /package/dist/model/{metas.js → infrastructure/metas.js} +0 -0
  174. /package/dist/{adapters → observability/adapters}/opentelemetry.js +0 -0
  175. /package/dist/{adapters → observability/adapters}/sentry.js +0 -0
  176. /package/dist/{services/report.d.ts → observability/services/reporting.d.ts} +0 -0
package/AGENTS.md CHANGED
@@ -20,18 +20,23 @@ it says what this package is responsible for and what it is not. This file is ho
20
20
 
21
21
  ## Where things go
22
22
 
23
- | Folder | What |
24
- | -------------------- | --------------------------------------------------------------------------------------------- |
25
- | `src/common/` | The values of the wire: ids, a day and an instant, `Decimal`, `Money`, `valuesOf` / `rebuilt` |
26
- | `src/client/` | The seam with a server: `Transport`, REST, memory, `entity()`, the client contract |
27
- | `src/model/` | Model definitions: `defineMeta`, from a JSON Schema, the store of known ones |
28
- | `src/services/` | `UseBoundedContext`, `Feature`, `ApplicationService`, the process, the execution |
29
- | `src/context/` | The `ContextStore` port, `memoryContexts`, and `Context` — one level of it |
30
- | `src/observability/` | The ports a platform plugs: `Tracer`, `ErrorReporter` |
31
- | `src/codegen/` | The generator and its CLI (`sincpro-client generate`) — the only place Node is allowed |
32
- | `src/adapters/` | Optional entries over a vendor (`./opentelemetry`, `./sentry`): the only place one is named |
33
- | `src/testing/` | `memoryTracer`, `memoryReporter` — the ports in memory, for tests |
34
- | `tests/` | vitest in Node; `tests/support/` holds the fake domains and forge's real contracts |
23
+ Each module is laid out as sincpro_framework's are: `domain/` (ports, types and rules),
24
+ `adapters/` (implementations of the ports), `services/` (what uses them), `infrastructure/`
25
+ (the process's state and technical support) and `entrypoint/` (the doors a caller uses). A
26
+ module has only the layers it needs, and says what it offers in its own `index.ts`.
27
+
28
+ | Folder | What |
29
+ | -------------------- | ----------------------------------------------------------------------------------------------- |
30
+ | `src/common/` | The values of the wire: ids, a day and an instant, `Decimal`, `Money`, `valuesOf` / `rebuilt` |
31
+ | `src/context/` | `ContextStore`, codec, `Execution` (domain); memory and plain codec; `Context`; the process's |
32
+ | `src/observability/` | Tracer/ErrorReporter/Logger ports; console, memory, OpenTelemetry, Sentry; loggers, reporting |
33
+ | `src/client/` | `Transport`, `entity()`, the contract (domain); REST, memory, Parquet; discovery, propagation |
34
+ | `src/model/` | Model definitions: `defineMeta`, from a JSON Schema, the store of known ones |
35
+ | `src/core/` | DTOs and events, bus, services, handlers, extension points; pipeline; `BoundedContext`; process |
36
+ | `src/event_driven/` | `Envelope`, `EventQueue`/`Subscribes` ports; `syncQueue`; `Publisher`, `Subscriber` |
37
+ | `src/codegen/` | The generator and its CLI (`sincpro-client generate`) — the only place Node is allowed |
38
+ | `src/testing/` | The public entry of the in-memory observability adapters |
39
+ | `tests/` | vitest in Node, one folder per module; `tests/support/` holds fakes and forge's contracts |
35
40
 
36
41
  ## Hard rules (the build enforces them — `make doctor`)
37
42
 
@@ -42,6 +47,11 @@ it says what this package is responsible for and what it is not. This file is ho
42
47
  and nowhere else; nothing an application runs imports `codegen/`.
43
48
  - **Self-imports, no extensions.** Inside `src/`, import `@sincpro/client-framework/<path>` —
44
49
  never a relative path, never `.ts`.
50
+ - **Domain inward.** A `domain/` folder imports only other `domain/` folders: no adapter,
51
+ service, infrastructure or entrypoint.
52
+ - **Vendors in their entry.** A vendor's SDK is imported only in its optional entry
53
+ (`observability/adapters/opentelemetry.ts`, `observability/adapters/sentry.ts`,
54
+ `client/adapters/parquet.ts`), and nothing in `src/` imports one.
45
55
 
46
56
  ## Conventions
47
57
 
@@ -55,17 +65,53 @@ it says what this package is responsible for and what it is not. This file is ho
55
65
  ## Known traps
56
66
 
57
67
  - **A run sees the instance through a view of its own.** Inside a method, each dependency
58
- (`this.client`), `this.context` and `this.feature` are that run's — bound to its execution. So a
68
+ (`this.client`), `this.context` and `this.featureBus` are that run's — bound to its execution. So a
59
69
  service declares no `#private` fields (they refuse the view); use TypeScript's `private`.
60
- - **A service is a singleton of its bounded context.** What a method writes on `this` lands on the
61
- instance, shared by the next call, the call beside it and every `with`/`scoped` variant — as in
70
+ - **A service is a singleton of its generation.** What a method writes on `this` lands on the
71
+ instance, shared by the next call, the call beside it and every `scoped` variant — as in
62
72
  sincpro_framework. What belongs to one request goes in its arguments or its context.
63
- - **A span is named by the registered name, not the class** (`register({ features: { Issues } })`
64
- → `project/Issues.close`, identity `project.Issues.close`): a production build renames classes,
65
- never the keys of an object.
66
- - **The dependencies are typed, not declared.** `BaseFeature<ProjectDependencies>` types
67
- `this.client` from an interface, which does not exist at run time; the bounded context answers
68
- whatever was added with `addDependency`. A dependency nobody added reads `undefined`.
73
+ - **A span uses stable service metadata, not constructor.name.** `serviceName = "Cancel"` gives
74
+ `project/Cancel`, identity `project.Cancel`. Commands and events route by exact constructor
75
+ identity. An event declares `static eventName` (its wire name) for the same reason; `name` is
76
+ reserved for it.
77
+ - **A replacement names what it replaces.** `feature(dto, { replaces: Service })` is refused
78
+ unless `Service` is registered now for `dto`; its spans carry `sincpro.replaces`. A service
79
+ meant to be subclassed declares `static readonly serviceName: string = "…"`, or the literal
80
+ type stops a subclass from renaming itself.
81
+ - **How events are carried is here; where they are kept is the platform's.** One subscriber
82
+ per bounded context; a `Subscriber` executes an event in each. `storedQueue` holds the rules —
83
+ store first, deliver in order, retry what may succeed, set aside what never will, never run a
84
+ context twice — over an `EventStore` the platform plugs (SQLite, IndexedDB, memory). A store
85
+ only keeps records; it never decides.
86
+ - **A circuit belongs where the dependency is.** `CircuitBreaker` (in the process's memory, the
87
+ backend's semantics: `COUNTED_KINDS`, half-open with one probe) guards a transport
88
+ (`guardedTransport`) or an adapter; its `CircuitOpen` declares `unavailable` and a wait, so
89
+ nothing else needs to know a circuit exists.
90
+ - **Delivery is at least once.** A stored event may run twice in a context after a crash; a
91
+ subscriber keys its effect by `this.execution.causationId` (the envelope's id). `maxAttempts`
92
+ sets aside what keeps failing. One process drains a store. `execution` is reserved.
93
+ - **A failure says its kind, whatever the wire.** `failureOf` reads a declared `failureKind`, the
94
+ `Problem` (the same over REST, JSON-RPC and gRPC), then a REST status. A transport of another
95
+ wire, or another backend's adapter, declares the kind with `kindOfGrpcCode` /
96
+ `kindOfJsonRpcCode` / `kindOfHttpStatus` — never a table of its own. Retrying, setting aside,
97
+ reporting and the outcome all follow from it.
98
+ - **A run can be cancelled.** `execute(…, { signal })`: it ends `aborted` at once, its Features
99
+ inherit the signal, `this.signal` reads it, and a bound transport sends it with every request.
100
+ `signal` is reserved, like `context` and `logger`.
101
+ - **No variable named `process`.** `make doctor` reads `process.` as Node outside `codegen/`.
102
+ - **A run belongs to a flow.** `correlationId` is shared by every run and event of one flow;
103
+ `causationId` names what directly caused it. A dependency carrying `BIND` (the `Publisher`)
104
+ is bound to the run, so what it publishes joins that flow with nothing passed by hand.
105
+ - **The pipeline is closed for modification, open for extension.** Change the flow in
106
+ `executeService` only for every run; what one application needs goes through an interceptor
107
+ (control, inside the run, keeps the class), a listener (observation, never changes the
108
+ answer, its failure ignored) or `extend`.
109
+ - **Dependency declarations do not register anything.** Service bases declare typed attributes
110
+ from a shared dependency interface. Only `addDependency` supplies runtime values; a missing
111
+ dependency fails explicitly.
112
+ - **Decorators are optional.** Function registration must work without any decorator
113
+ transform. Standard and legacy (`experimentalDecorators`) fixtures are compiled explicitly in
114
+ tests; do not claim Metro (Babel) or web bundler support without testing those configurations.
69
115
  - **The trace reaches the wire through the dependencies.** A dependency is bound to the run when it
70
116
  is a `Transport` or carries `BIND` (every generated client does, through `bindable`); anything
71
117
  else is handed as it is. A client built elsewhere and closed over sends no trace.
@@ -76,20 +122,44 @@ it says what this package is responsible for and what it is not. This file is ho
76
122
  - **A store's `load` may answer a new object each time** (one that parses JSON does): `Context`
77
123
  keeps one object per `version`, so a screen can hand `values` to `useSyncExternalStore`. A store
78
124
  that changes without moving the version is a broken store.
125
+ - **The generated client never names a wire.** A method is `transport.call(operations.X, input,
126
+ options)`: no route, no verb, no RPC method. The contract's bindings (`rest`, `rpc`, `grpc`,
127
+ `mcp`) are data in `operations`; an `OperationTransport` adapter reads the one of its wire
128
+ (`restOperations`, `jsonRpcOperations`, a platform's gRPC) and throws `OperationNotPublished`
129
+ for the rest. A run binds an `OperationTransport` as it binds a `Transport`: headers and signal.
130
+ The REST `Transport` stays for `restOperations` and for screens (`entity()`, pages, drafts).
131
+ - **What is generated is types, never classes.** A generated DTO is the remote contract: a
132
+ class extending `Command` could be registered on the local bus, which is what keeps a remote
133
+ operation and a local use case apart. Runtime knowledge goes in data (`operations`, `events`);
134
+ validating or converting what arrives would be a generated reader, not a class.
135
+ - **Events come only from the contract.** The generator writes event types and `DomainEvent`
136
+ only when the backend exported its contract with `--events`; a contract without them writes
137
+ the same bytes as before.
79
138
  - **What the generator writes is compared byte for byte** (`tests/support/forge-contract/`, out
80
139
  of prettier and eslint). After changing the generator on purpose, run `make codegen-fixtures`
81
140
  and read the diff.
82
- - **A module name the generator writes is built in parts** (`codegen/names.ts`, `FRAMEWORK`): the
141
+ - **A module name the generator writes is built in parts** (`codegen/domain/names.ts`, `FRAMEWORK`): the
83
142
  build's `tsc-alias` rewrites this package's specifiers inside strings too.
84
143
  - **An adapter wraps what the application built, never builds it.** `openTelemetryTracer` takes
85
144
  the app's tracer, `sentryReporter` the app's client: no `register()`, no `Sentry.init()`, no
86
145
  endpoint or DSN here, nothing read from the environment. A vendor's SDK is imported only in
87
- `src/adapters/`, and nothing else imports an adapter (`make doctor`).
146
+ its optional entry, and nothing else imports one (`make doctor`).
88
147
  - **A span nothing records claims no trace.** OpenTelemetry's API with no provider answers an
89
148
  all-zeros span: the adapter gives no ids, so no `traceparent` of zeros reaches the server.
90
149
  - **Reported means unexpected.** A `TransportError` the server answered with a 4xx is `refused`,
91
- an `AbortError` is `aborted`; neither is reported (`outcomeOf`). Change that with
92
- `ProcessOptions.outcomeOf`, never by catching in a Feature.
150
+ an `AbortError` is `aborted`; neither is reported (`outcomeOf`). A bounded context adds its own
151
+ with `expectErrors(...)`; a process replaces the rule with `ProcessOptions.outcomeOf`. Never
152
+ catch in a Feature to hide a failure.
153
+ - **An error handler answers; it does not observe.** The failure is recorded where it happened —
154
+ span, log line, report — before the layer's handlers run, as in sincpro_framework. A handler
155
+ returns the answer or throws to the next; the global chain wraps only `execute` and `scoped`.
156
+ - **The application layer imports no logger.** A bounded context has `project.logger`; a run
157
+ hands `this.logger`, bound to its execution. Only `logger` (infrastructure, the global one) and
158
+ `loggerAdapter` are predefined; injected as a dependency they bind to the run (`BIND`), imported
159
+ at module level they do not. No logger is predefined for an application layer. Console is the
160
+ default engine; `configureProcess({ logger })` and `configureLogger(...)` take the same
161
+ `LoggerConfiguration`. The host chooses production explicitly. Never log DTOs, results, full
162
+ session context or raw errors automatically.
93
163
 
94
164
  - **Only what is transversal comes here.** A value's wire form (a decimal as text, a day never
95
165
  an instant, a money with its currency) is the backend's contract, the same on every platform,
package/MANIFESTO.md CHANGED
@@ -25,28 +25,34 @@ server-side render, in a script and in a test, unchanged.**
25
25
 
26
26
  ## What it is responsible for
27
27
 
28
- 1. **Services.** A `Feature` is a facade over one part of the domain, each public method a use
29
- case; an `ApplicationService` composes the Features of its own bounded context — never another
30
- ApplicationService. All of a client's logic lives in them, with their dependencies typed and
31
- injected by their bounded context (`this.client`, `this.session`).
32
- 2. **Interception.** Every public method of a use case passes through interceptors, and that is
33
- what makes the defaults free: the trace, the error report, the context. The author of a
34
- Feature writes none of it.
35
- 3. **Observability, as ports.** `Tracer` and `ErrorReporter` are the contract: one span per use
36
- case, named `<bounded context>/<Service>.<method>` like the backend's; the unexpected reported
37
- with its trace id; what the server already explained (a validation, a conflict) not reported.
28
+ 1. **Services.** A `Feature` executes a registered Command through `execute(dto)`;
29
+ an `ApplicationService` composes its context's Features through `featureBus`, never another
30
+ ApplicationService. Dependencies are typed attributes supplied by the bounded context.
31
+ 2. **Execution.** Every Command and event enters one execution pipeline, defined here: context,
32
+ chain, span, log lines, outcome, report, error handlers — not handwritten by each service. An
33
+ application changes it from outside through its extension points, as the backend's
34
+ `bus_pipeline`: interceptors control a run (look, veto, adjust, answer); listeners hear it
35
+ start, answer or fail, and never change it; an extension wires a component in one place.
36
+ 3. **Observability, as ports.** `Tracer`, `ErrorReporter` and `Logger` are the contracts: one span per use
37
+ case, named `<bounded context>/<serviceName>`; the unexpected reported
38
+ with its trace id; what the server already explained (a validation, a conflict), or what a
39
+ bounded context declares expected, not reported.
38
40
  The platform plugs the implementation — OpenTelemetry, GlitchTip, a queue on the phone, memory
39
- in a test — once, for the process (`configureProcess`).
41
+ in a test — once, for the process (`configureProcess`). Structured logs are tagged by layer
42
+ (infrastructure, adapter, bounded context, ApplicationService, Feature) and by execution;
43
+ console filtering never controls another sink's delivery.
40
44
  4. **Context.** The levels a call sees — the process, its bounded context, a scope — read once
41
45
  where a gesture starts and inherited by everything it runs, and what travels to the server with
42
46
  it (`traceparent`, `sincpro-context`). Where the values live is a port (`ContextStore`) each
43
47
  platform implements with its own technology; the execution is carried by each call, never kept
44
48
  in a global stack: two calls at once never mix their parents.
45
49
  5. **Bounded contexts.** Set up like the backend's — its dependencies (`addDependency`), its
46
- Features and ApplicationServices, registered once — and used through one light object:
47
- `project.Issues.close(input)`. An application is the set of bounded contexts it loads; they
48
- never import each other's insides.
49
- 6. **The client.** The `Transport` port and its implementations (REST, memory), `entity()` with
50
+ Features and ApplicationServices, registered for class DTOs — and called as
51
+ `project.execute(new Command(...))`. The bounded context is the bus. An application is the set
52
+ of bounded contexts it loads; they never import each other's insides.
53
+ 6. **The client.** The `OperationTransport` port — an operation called by who it is, over any
54
+ wire — with its REST and JSON-RPC adapters (a platform adds gRPC or its own); the `Transport`
55
+ port and its implementations (REST, memory), `entity()` with
50
56
  typed commands and answers, the service's client contract, model definitions read once from
51
57
  the server and the server's `Problem`.
52
58
  7. **The values of the wire.** How every value travels, as the backend writes it — a decimal as
@@ -54,9 +60,25 @@ server-side render, in a script and in a test, unchanged.**
54
60
  and the exact arithmetic a client needs on them (`Decimal`, `Money`). Only the wire form and
55
61
  what follows from it: showing a value to a person is each platform's.
56
62
  8. **The generator.** From a service's client contract to each domain's types, client and
57
- entities — generated, never edited, and as platform-neutral as everything else here.
63
+ entities — and its domain events when the contract carries them —, generated as types,
64
+ never edited, and as platform-neutral as everything else here.
58
65
  9. **Testing.** An in-memory transport, tracer and reporter, so a use case is proven in Node with
59
66
  nothing else.
67
+ 10. **The command core (`./core`).** The single model matching the backend's application
68
+ abstractions: class DTOs and Responses, a bounded context that is a bus, Features and
69
+ ApplicationServices registered for DTO classes, attribute-injected dependencies and a
70
+ feature bus for composition, domain events executed by their subscribers, replacements,
71
+ and the backend's error handlers — global, feature and application service — with the
72
+ errors a bounded context expects. The internal engine uses
73
+ the injected process ports. Remote clients and mapping remain distinct.
74
+ 11. **Event-driven, as ports (`event_driven`).** How every platform publishes domain events and
75
+ subscribes to them the same way: the envelope that travels beside an event (its flow and
76
+ its cause), the `EventQueue` and `Subscribes` ports, a `Publisher` a Feature holds, a
77
+ `Subscriber` over the bounded contexts that hear, the queue that delivers in the call, and
78
+ the stored queue's rules over an `EventStore` the platform plugs. The guidelines, the rules
79
+ and the contracts are here; where events are kept is the platform's.
80
+ 12. **Resilience.** What a failure is (`failureOf`, the backend's kinds on every wire) and the
81
+ backend's circuit breaker, guarding any transport or adapter.
60
82
 
61
83
  ## What it is not
62
84
 
@@ -73,9 +95,13 @@ server-side render, in a script and in a test, unchanged.**
73
95
  application built — its provider, its client, with its endpoint and its DSN. It initialises
74
96
  nothing and registers nothing global; an application that does not import them installs
75
97
  nothing and ships nothing, and without a tracer or a reporter everything runs the same.
76
- - **Not where business rules live.** The server keeps them; a client reads what it publishes.
77
- - **Not an event bus**, and not a dispatcher: a use case is executed by calling it, and the
78
- interceptors are what make that call traced and reported.
98
+ - **Not where application-specific business rules live.** The package supplies execution
99
+ abstractions; a registered use case may delegate to a server or use a local domain. A remote
100
+ adapter does not authorize reimplementing the server's rules.
101
+ - **Not a durable event delivery system.** It defines how events are published, carried and
102
+ executed — the ports, the envelope, the in-call queue — so web and mobile do it alike; storing
103
+ them, retrying, a dead-letter queue, waiting for a connection, other tabs, a broker are the
104
+ platform's adapters behind `EventQueue`.
79
105
 
80
106
  ## What we believe
81
107
 
@@ -85,17 +111,18 @@ server-side render, in a script and in a test, unchanged.**
85
111
  2. **The backend's words.** Bounded context, Feature, ApplicationService, dependencies, context,
86
112
  process, the span name — the same as sincpro_framework, so whoever knows the backend already
87
113
  knows the client.
88
- 3. **Nothing escapes the interceptors.** Reusing a use case is calling it through its bounded
89
- context. A helper that wraps one skips the trace and the report — that is how an incident ends
90
- up with nothing to look at.
114
+ 3. **Nothing escapes execution.** Reusing a use case means dispatching its Command through the
115
+ bounded context or feature bus. Directly constructing and calling a service bypasses its
116
+ execution context and instrumentation.
91
117
  4. **Context travels with the call.** Never in a global stack: clients run many things at once.
92
118
  5. **A port where there are two implementations, a class where there is one.** `Transport` has
93
119
  REST and memory; `Tracer` has a platform's and a test's; `ContextStore` has the browser's, the
94
120
  phone's and memory.
95
121
  6. **Generated, never edited.** Applications extend by composition; regenerating never loses
96
122
  work.
97
- 7. **The server is the source of truth.** A client reads the model, the preview and the refusal,
98
- and never re-implements a rule.
123
+ 7. **Authority is explicit.** A generated client reads the server's model and refusal and never
124
+ authorizes duplicating server rules. A local domain can own local rules; synchronization and
125
+ conflict policy are separate from command delivery.
99
126
  8. **Small, and paid for only when used.** No side effects at import, tree-shakable, and no
100
127
  dependency it can avoid.
101
128
  9. **Grow by evidence.** Nothing before its second use; every change ships with a test that fails
@@ -107,8 +134,8 @@ server-side render, in a script and in a test, unchanged.**
107
134
  @sincpro/criteria the contract with the server: Criteria, Meta, Page, Problem
108
135
  ▲
109
136
  @sincpro/client-framework this package — platform-neutral
110
- │ UseBoundedContext · Feature · ApplicationService · interceptors
111
- │ the process · the context · Tracer, ErrorReporter and ContextStore ports
137
+ │ BoundedContext · Command · Feature · ApplicationService · execution
138
+ │ the process · the context · Tracer, ErrorReporter, Logger and ContextStore ports
112
139
  │ Transport · entity() · client contract · model definitions · ids
113
140
  │ the generator · in-memory doubles for tests
114
141
  ▲
@@ -119,22 +146,58 @@ server-side render, in a script and in a test, unchanged.**
119
146
 
120
147
  Dependencies point down only. This package knows nothing above it.
121
148
 
122
- ## Where it stands
123
-
124
- **The first cut is here.** The client and the generator came out of @sincpro/web-framework,
125
- where they already ran without the DOM. `Feature` and `ApplicationService` came from
126
- @sincpro/mobile, with each call carrying its own execution instead of a global stack. And the
127
- ports for tracing and error reporting are in, with the trace and the context written on every
128
- request a use case sends.
149
+ **What goes where.** client-framework + web-framework is to a web application what
150
+ sincpro_framework is to a backend; client-framework + @sincpro/mobile, the same for a phone. A
151
+ piece web and mobile can both use, and that needs no platform, comes here — as a port, its rules
152
+ and a memory adapter. A piece only one platform needs, or that needs a platform's technology, is
153
+ that platform's — an adapter of a port. An application's business is the application's. Nothing
154
+ is replicated: what differs by nature (a phone's SQLite domain, a browser's entity screens) stays
155
+ different, and the platforms meet here only through the ports.
129
156
 
130
- **Then the bounded context**, set up as the backend sets one up: a dependency type that types every
131
- service's `this.client`, `addDependency`, the services registered by name and used as
132
- `project.Issues.close(input)` and `this.feature.Issues`; the process configured once in `main`
133
- (`configureProcess`); and the context as a port, so the browser and the phone keep it each with
134
- their own technology behind the same door.
157
+ ## Where it stands
135
158
 
136
- **Next:** @sincpro/mobile on this package, and the screens' side in @sincpro/web-framework — the
137
- cache of reads, invalidated by the writes a use case sends.
159
+ **The application layer is the backend's.** A bounded context executes class Commands through
160
+ one pipeline — context, span, log lines, outcome, report — with its dependencies injected,
161
+ its ApplicationServices composing Features through `featureBus`, and the backend's error
162
+ handlers: a failure is observed where it happened, then answered or handed on. Screen requests
163
+ and scaffolded applications run through the same pipeline.
164
+
165
+ **Observability is ports, plugged once.** Tracing, error reporting and logging are ports the
166
+ process configures; the console is the default log engine, OpenTelemetry and Sentry optional
167
+ entries that wrap what the application built. The application layer imports no logger: a
168
+ bounded context has its own, and a run hands `this.logger`, bound to its execution; only the
169
+ infrastructure and the adapters have a predefined one.
170
+
171
+ **The client and the generator read the backend's contract.** Types per model, operation and —
172
+ when the contract is exported with them — domain event; a client whose methods call each
173
+ operation by who it is, over the `OperationTransport` the application injects — REST, JSON-RPC,
174
+ a platform's gRPC —; the entities screens use. What is generated is types, never classes, and
175
+ never names a wire: the contract's bindings are data, read by the adapter of each wire.
176
+
177
+ **Each module is laid out as the backend's.** `domain/`, `adapters/`, `services/`,
178
+ `infrastructure/`, `entrypoint/`, only those a module needs; `domain/` depends only on `domain/`.
179
+
180
+ **The pipeline is the core's, and open.** The flow of a run is defined once; interceptors,
181
+ listeners and extensions change or hear it from outside, without touching a service.
182
+
183
+ **Events travel the same way on every platform.** A Feature publishes through a `Publisher`;
184
+ a `Subscriber` executes the event in every bounded context that hears it, in the same flow
185
+ (`correlationId`, caused by the run that published it). In the call (`syncQueue`), or stored
186
+ first and delivered by the backend's store-and-forward rules (`storedQueue`) over the
187
+ `EventStore` each platform plugs — a SQLite table, IndexedDB, memory.
188
+
189
+ **A failure means the same thing everywhere.** `failureOf` reads any error — whatever the wire,
190
+ whatever backend — in the backend's kinds and next steps; retrying, setting aside, reporting and
191
+ a screen's answer follow from it. A run can be cancelled, and its requests with it. The
192
+ backend's circuit breaker guards any transport.
193
+
194
+ **Consumers have a guide.** The `sincpro-client-framework` skill says, for an application and for
195
+ a platform's framework, what goes where and how each thing is done.
196
+
197
+ **Next:** web and mobile adopt it in their own releases — mobile's SQLite `EventStore` and gRPC or
198
+ JSON-RPC wire, web's in-memory or IndexedDB store and its hooks —, declaring this package as a
199
+ peer dependency; events from the server live, with the backend's server-sent events; generated
200
+ readers that validate and convert what arrives, when a use needs them.
138
201
 
139
202
  ## How we decide
140
203