@sincpro/client-framework 0.5.0 → 1.0.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 (134) hide show
  1. package/AGENTS.md +54 -25
  2. package/MANIFESTO.md +59 -39
  3. package/README.md +329 -328
  4. package/dist/client/{memory.d.ts → adapters/memory.d.ts} +1 -1
  5. package/dist/client/{memory.js → adapters/memory.js} +7 -5
  6. package/dist/client/adapters/parquet.d.ts +50 -0
  7. package/dist/client/adapters/parquet.js +151 -0
  8. package/dist/client/{rest.d.ts → adapters/rest.d.ts} +7 -1
  9. package/dist/client/{rest.js → adapters/rest.js} +48 -3
  10. package/dist/client/{entity.d.ts → domain/entity.d.ts} +22 -11
  11. package/dist/client/{entity.js → domain/entity.js} +16 -1
  12. package/dist/client/{transport.d.ts → domain/transport.d.ts} +37 -1
  13. package/dist/client/{transport.js → domain/transport.js} +6 -0
  14. package/dist/client/index.d.ts +16 -0
  15. package/dist/client/index.js +16 -0
  16. package/dist/client/services/propagation.d.ts +32 -0
  17. package/dist/{services/execution.js → client/services/propagation.js} +8 -21
  18. package/dist/client/{retry.js → services/retry.js} +1 -1
  19. package/dist/codegen/{metamodel.d.ts → domain/metamodel.d.ts} +21 -2
  20. package/dist/codegen/{names.d.ts → domain/names.d.ts} +2 -2
  21. package/dist/codegen/{names.js → domain/names.js} +3 -3
  22. package/dist/codegen/{cli.js → entrypoint/cli.js} +9 -8
  23. package/dist/codegen/index.d.ts +7 -7
  24. package/dist/codegen/index.js +5 -10
  25. package/dist/codegen/{client.d.ts → services/client.d.ts} +3 -1
  26. package/dist/codegen/{client.js → services/client.js} +34 -5
  27. package/dist/codegen/services/domain.d.ts +20 -0
  28. package/dist/codegen/{domain.js → services/domain.js} +121 -23
  29. package/dist/codegen/{entities.d.ts → services/entities.d.ts} +4 -2
  30. package/dist/codegen/{entities.js → services/entities.js} +34 -4
  31. package/dist/codegen/{rest.d.ts → services/rest.d.ts} +1 -1
  32. package/dist/codegen/{scaffold.d.ts → services/scaffold.d.ts} +2 -2
  33. package/dist/codegen/{scaffold.js → services/scaffold.js} +31 -36
  34. package/dist/common/collection.d.ts +110 -0
  35. package/dist/common/collection.js +169 -0
  36. package/dist/common/index.d.ts +10 -0
  37. package/dist/common/index.js +10 -0
  38. package/dist/context/adapters/memory.d.ts +6 -0
  39. package/dist/context/adapters/memory.js +34 -0
  40. package/dist/context/adapters/plain-codec.d.ts +3 -0
  41. package/dist/context/adapters/plain-codec.js +16 -0
  42. package/dist/context/{codec.d.ts → domain/codec.d.ts} +0 -1
  43. package/dist/context/{codec.js → domain/codec.js} +0 -14
  44. package/dist/context/domain/execution.d.ts +33 -0
  45. package/dist/context/domain/execution.js +11 -0
  46. package/dist/context/{store.d.ts → domain/store.d.ts} +0 -5
  47. package/dist/context/{store.js → domain/store.js} +1 -34
  48. package/dist/context/{context.d.ts → entrypoint/context.d.ts} +1 -1
  49. package/dist/context/index.d.ts +17 -0
  50. package/dist/context/index.js +15 -0
  51. package/dist/context/infrastructure/process.d.ts +13 -0
  52. package/dist/context/infrastructure/process.js +35 -0
  53. package/dist/core/domain/bus.d.ts +18 -0
  54. package/dist/core/domain/bus.js +18 -0
  55. package/dist/core/domain/dto.d.ts +32 -0
  56. package/dist/core/domain/dto.js +20 -0
  57. package/dist/core/domain/errors.d.ts +18 -0
  58. package/dist/core/domain/errors.js +1 -0
  59. package/dist/core/domain/services.d.ts +37 -0
  60. package/dist/core/domain/services.js +28 -0
  61. package/dist/core/entrypoint/bounded-context.d.ts +70 -0
  62. package/dist/core/entrypoint/bounded-context.js +252 -0
  63. package/dist/core/entrypoint/screens.d.ts +6 -0
  64. package/dist/core/entrypoint/screens.js +136 -0
  65. package/dist/core/index.d.ts +17 -0
  66. package/dist/core/index.js +16 -0
  67. package/dist/core/infrastructure/process.d.ts +56 -0
  68. package/dist/core/infrastructure/process.js +35 -0
  69. package/dist/core/services/binding.d.ts +6 -0
  70. package/dist/core/services/binding.js +13 -0
  71. package/dist/core/services/error-handlers.d.ts +11 -0
  72. package/dist/core/services/error-handlers.js +21 -0
  73. package/dist/core/services/execution.d.ts +35 -0
  74. package/dist/core/services/execution.js +137 -0
  75. package/dist/index.d.ts +16 -36
  76. package/dist/index.js +16 -36
  77. package/dist/model/{meta.d.ts → domain/meta.d.ts} +1 -0
  78. package/dist/model/index.d.ts +10 -0
  79. package/dist/model/index.js +10 -0
  80. package/dist/model/{metas.d.ts → infrastructure/metas.d.ts} +1 -1
  81. package/dist/model/{schema.d.ts → services/schema.d.ts} +1 -1
  82. package/dist/model/{schema.js → services/schema.js} +1 -1
  83. package/dist/observability/adapters/console.d.ts +12 -0
  84. package/dist/observability/adapters/console.js +35 -0
  85. package/dist/observability/adapters/memory.d.ts +41 -0
  86. package/dist/observability/adapters/memory.js +49 -0
  87. package/dist/{adapters → observability/adapters}/opentelemetry.d.ts +1 -1
  88. package/dist/{adapters → observability/adapters}/sentry.d.ts +1 -1
  89. package/dist/observability/domain/errors.d.ts +23 -0
  90. package/dist/observability/domain/errors.js +8 -0
  91. package/dist/observability/domain/logging.d.ts +42 -0
  92. package/dist/observability/domain/logging.js +1 -0
  93. package/dist/observability/domain/tracing.d.ts +49 -0
  94. package/dist/observability/domain/tracing.js +20 -0
  95. package/dist/observability/index.d.ts +16 -0
  96. package/dist/observability/index.js +4 -0
  97. package/dist/observability/infrastructure/registry.d.ts +21 -0
  98. package/dist/observability/infrastructure/registry.js +18 -0
  99. package/dist/observability/services/logging.d.ts +27 -0
  100. package/dist/observability/services/logging.js +105 -0
  101. package/dist/observability/services/outcome.d.ts +12 -0
  102. package/dist/observability/services/outcome.js +20 -0
  103. package/dist/{services/report.js → observability/services/reporting.js} +4 -3
  104. package/dist/testing/index.d.ts +3 -33
  105. package/dist/testing/index.js +5 -37
  106. package/package.json +25 -8
  107. package/dist/codegen/domain.d.ts +0 -15
  108. package/dist/observability/ports.d.ts +0 -65
  109. package/dist/observability/ports.js +0 -21
  110. package/dist/services/bounded-context.d.ts +0 -79
  111. package/dist/services/bounded-context.js +0 -261
  112. package/dist/services/execution.d.ts +0 -63
  113. package/dist/services/feature.d.ts +0 -85
  114. package/dist/services/feature.js +0 -86
  115. package/dist/services/process.d.ts +0 -60
  116. package/dist/services/process.js +0 -57
  117. package/dist/services/screens.d.ts +0 -2
  118. package/dist/services/screens.js +0 -67
  119. /package/dist/client/{contract.d.ts → domain/contract.d.ts} +0 -0
  120. /package/dist/client/{contract.js → domain/contract.js} +0 -0
  121. /package/dist/client/{discovery.d.ts → services/discovery.d.ts} +0 -0
  122. /package/dist/client/{discovery.js → services/discovery.js} +0 -0
  123. /package/dist/client/{retry.d.ts → services/retry.d.ts} +0 -0
  124. /package/dist/codegen/{metamodel.js → domain/metamodel.js} +0 -0
  125. /package/dist/codegen/{cli.d.ts → entrypoint/cli.d.ts} +0 -0
  126. /package/dist/codegen/{check.d.ts → services/check.d.ts} +0 -0
  127. /package/dist/codegen/{check.js → services/check.js} +0 -0
  128. /package/dist/codegen/{rest.js → services/rest.js} +0 -0
  129. /package/dist/context/{context.js → entrypoint/context.js} +0 -0
  130. /package/dist/model/{meta.js → domain/meta.js} +0 -0
  131. /package/dist/model/{metas.js → infrastructure/metas.js} +0 -0
  132. /package/dist/{adapters → observability/adapters}/opentelemetry.js +0 -0
  133. /package/dist/{adapters → observability/adapters}/sentry.js +0 -0
  134. /package/dist/{services/report.d.ts → observability/services/reporting.d.ts} +0 -0
package/AGENTS.md CHANGED
@@ -20,18 +20,22 @@ 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, bus, Feature/ApplicationService, error handlers; pipeline; `BoundedContext`; the process |
36
+ | `src/codegen/` | The generator and its CLI (`sincpro-client generate`) — the only place Node is allowed |
37
+ | `src/testing/` | The public entry of the in-memory observability adapters |
38
+ | `tests/` | vitest in Node, one folder per module; `tests/support/` holds fakes and forge's contracts |
35
39
 
36
40
  ## Hard rules (the build enforces them — `make doctor`)
37
41
 
@@ -42,6 +46,11 @@ it says what this package is responsible for and what it is not. This file is ho
42
46
  and nowhere else; nothing an application runs imports `codegen/`.
43
47
  - **Self-imports, no extensions.** Inside `src/`, import `@sincpro/client-framework/<path>` —
44
48
  never a relative path, never `.ts`.
49
+ - **Domain inward.** A `domain/` folder imports only other `domain/` folders: no adapter,
50
+ service, infrastructure or entrypoint.
51
+ - **Vendors in their entry.** A vendor's SDK is imported only in its optional entry
52
+ (`observability/adapters/opentelemetry.ts`, `observability/adapters/sentry.ts`,
53
+ `client/adapters/parquet.ts`), and nothing in `src/` imports one.
45
54
 
46
55
  ## Conventions
47
56
 
@@ -55,17 +64,19 @@ it says what this package is responsible for and what it is not. This file is ho
55
64
  ## Known traps
56
65
 
57
66
  - **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
67
+ (`this.client`), `this.context` and `this.featureBus` are that run's — bound to its execution. So a
59
68
  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
69
+ - **A service is a singleton of its generation.** What a method writes on `this` lands on the
70
+ instance, shared by the next call, the call beside it and every `scoped` variant — as in
62
71
  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`.
72
+ - **A span uses stable service metadata, not constructor.name.** `serviceName = "Cancel"` gives
73
+ `project/Cancel`, identity `project.Cancel`. Commands route by exact constructor identity.
74
+ - **Dependency declarations do not register anything.** Service bases declare typed attributes
75
+ from a shared dependency interface. Only `addDependency` supplies runtime values; a missing
76
+ dependency fails explicitly.
77
+ - **Decorators are optional.** Function registration must work without any decorator
78
+ transform. Standard and legacy (`experimentalDecorators`) fixtures are compiled explicitly in
79
+ tests; do not claim Metro (Babel) or web bundler support without testing those configurations.
69
80
  - **The trace reaches the wire through the dependencies.** A dependency is bound to the run when it
70
81
  is a `Transport` or carries `BIND` (every generated client does, through `bindable`); anything
71
82
  else is handed as it is. A client built elsewhere and closed over sends no trace.
@@ -76,20 +87,38 @@ it says what this package is responsible for and what it is not. This file is ho
76
87
  - **A store's `load` may answer a new object each time** (one that parses JSON does): `Context`
77
88
  keeps one object per `version`, so a screen can hand `values` to `useSyncExternalStore`. A store
78
89
  that changes without moving the version is a broken store.
90
+ - **What is generated is types, never classes.** A generated DTO is the remote contract: a
91
+ class extending `Command` could be registered on the local bus, which is what keeps a remote
92
+ operation and a local use case apart. Runtime knowledge goes in data (`operations`, `events`);
93
+ validating or converting what arrives would be a generated reader, not a class.
94
+ - **Events come only from the contract.** The generator writes event types and `DomainEvent`
95
+ only when the backend exported its contract with `--events`; a contract without them writes
96
+ the same bytes as before.
79
97
  - **What the generator writes is compared byte for byte** (`tests/support/forge-contract/`, out
80
98
  of prettier and eslint). After changing the generator on purpose, run `make codegen-fixtures`
81
99
  and read the diff.
82
- - **A module name the generator writes is built in parts** (`codegen/names.ts`, `FRAMEWORK`): the
100
+ - **A module name the generator writes is built in parts** (`codegen/domain/names.ts`, `FRAMEWORK`): the
83
101
  build's `tsc-alias` rewrites this package's specifiers inside strings too.
84
102
  - **An adapter wraps what the application built, never builds it.** `openTelemetryTracer` takes
85
103
  the app's tracer, `sentryReporter` the app's client: no `register()`, no `Sentry.init()`, no
86
104
  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`).
105
+ its optional entry, and nothing else imports one (`make doctor`).
88
106
  - **A span nothing records claims no trace.** OpenTelemetry's API with no provider answers an
89
107
  all-zeros span: the adapter gives no ids, so no `traceparent` of zeros reaches the server.
90
108
  - **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.
109
+ an `AbortError` is `aborted`; neither is reported (`outcomeOf`). A bounded context adds its own
110
+ with `expectErrors(...)`; a process replaces the rule with `ProcessOptions.outcomeOf`. Never
111
+ catch in a Feature to hide a failure.
112
+ - **An error handler answers; it does not observe.** The failure is recorded where it happened —
113
+ span, log line, report — before the layer's handlers run, as in sincpro_framework. A handler
114
+ returns the answer or throws to the next; the global chain wraps only `execute` and `scoped`.
115
+ - **The application layer imports no logger.** A bounded context has `project.logger`; a run
116
+ hands `this.logger`, bound to its execution. Only `logger` (infrastructure, the global one) and
117
+ `loggerAdapter` are predefined; injected as a dependency they bind to the run (`BIND`), imported
118
+ at module level they do not. No logger is predefined for an application layer. Console is the
119
+ default engine; `configureProcess({ logger })` and `configureLogger(...)` take the same
120
+ `LoggerConfiguration`. The host chooses production explicitly. Never log DTOs, results, full
121
+ session context or raw errors automatically.
93
122
 
94
123
  - **Only what is transversal comes here.** A value's wire form (a decimal as text, a day never
95
124
  an instant, a money with its currency) is the backend's contract, the same on every platform,
package/MANIFESTO.md CHANGED
@@ -25,27 +25,28 @@ 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 enters one internal execution pipeline. Context, tracing,
32
+ outcomes and error reporting are supplied there, not handwritten by each service.
33
+ 3. **Observability, as ports.** `Tracer`, `ErrorReporter` and `Logger` are the contracts: one span per use
34
+ case, named `<bounded context>/<serviceName>`; the unexpected reported
35
+ with its trace id; what the server already explained (a validation, a conflict), or what a
36
+ bounded context declares expected, not reported.
38
37
  The platform plugs the implementation — OpenTelemetry, GlitchTip, a queue on the phone, memory
39
- in a test — once, for the process (`configureProcess`).
38
+ in a test — once, for the process (`configureProcess`). Structured logs are tagged by layer
39
+ (infrastructure, adapter, bounded context, ApplicationService, Feature) and by execution;
40
+ console filtering never controls another sink's delivery.
40
41
  4. **Context.** The levels a call sees — the process, its bounded context, a scope — read once
41
42
  where a gesture starts and inherited by everything it runs, and what travels to the server with
42
43
  it (`traceparent`, `sincpro-context`). Where the values live is a port (`ContextStore`) each
43
44
  platform implements with its own technology; the execution is carried by each call, never kept
44
45
  in a global stack: two calls at once never mix their parents.
45
46
  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.
47
+ Features and ApplicationServices, registered for class DTOs — and called as
48
+ `project.execute(new Command(...))`. The bounded context is the bus. An application is the set
49
+ of bounded contexts it loads; they never import each other's insides.
49
50
  6. **The client.** The `Transport` port and its implementations (REST, memory), `entity()` with
50
51
  typed commands and answers, the service's client contract, model definitions read once from
51
52
  the server and the server's `Problem`.
@@ -54,9 +55,16 @@ server-side render, in a script and in a test, unchanged.**
54
55
  and the exact arithmetic a client needs on them (`Decimal`, `Money`). Only the wire form and
55
56
  what follows from it: showing a value to a person is each platform's.
56
57
  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.
58
+ entities — and its domain events when the contract carries them —, generated as types,
59
+ never edited, and as platform-neutral as everything else here.
58
60
  9. **Testing.** An in-memory transport, tracer and reporter, so a use case is proven in Node with
59
61
  nothing else.
62
+ 10. **The command core (`./core`).** The single model matching the backend's application
63
+ abstractions: class DTOs and Responses, a bounded context that is a bus, Features and
64
+ ApplicationServices registered for DTO classes, attribute-injected dependencies and a
65
+ feature bus for composition, and the backend's error handlers — global, feature and
66
+ application service — with the errors a bounded context expects. The internal engine uses
67
+ the injected process ports. Remote clients and mapping remain distinct.
60
68
 
61
69
  ## What it is not
62
70
 
@@ -73,9 +81,11 @@ server-side render, in a script and in a test, unchanged.**
73
81
  application built — its provider, its client, with its endpoint and its DSN. It initialises
74
82
  nothing and registers nothing global; an application that does not import them installs
75
83
  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.
84
+ - **Not where application-specific business rules live.** The package supplies execution
85
+ abstractions; a registered use case may delegate to a server or use a local domain. A remote
86
+ adapter does not authorize reimplementing the server's rules.
87
+ - **Not an event delivery system.** The command dispatcher in `./core` executes use cases.
88
+ An event contract does not supply a publisher, queue, outbox, broker or synchronization engine.
79
89
 
80
90
  ## What we believe
81
91
 
@@ -85,17 +95,18 @@ server-side render, in a script and in a test, unchanged.**
85
95
  2. **The backend's words.** Bounded context, Feature, ApplicationService, dependencies, context,
86
96
  process, the span name — the same as sincpro_framework, so whoever knows the backend already
87
97
  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.
98
+ 3. **Nothing escapes execution.** Reusing a use case means dispatching its Command through the
99
+ bounded context or feature bus. Directly constructing and calling a service bypasses its
100
+ execution context and instrumentation.
91
101
  4. **Context travels with the call.** Never in a global stack: clients run many things at once.
92
102
  5. **A port where there are two implementations, a class where there is one.** `Transport` has
93
103
  REST and memory; `Tracer` has a platform's and a test's; `ContextStore` has the browser's, the
94
104
  phone's and memory.
95
105
  6. **Generated, never edited.** Applications extend by composition; regenerating never loses
96
106
  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.
107
+ 7. **Authority is explicit.** A generated client reads the server's model and refusal and never
108
+ authorizes duplicating server rules. A local domain can own local rules; synchronization and
109
+ conflict policy are separate from command delivery.
99
110
  8. **Small, and paid for only when used.** No side effects at import, tree-shakable, and no
100
111
  dependency it can avoid.
101
112
  9. **Grow by evidence.** Nothing before its second use; every change ships with a test that fails
@@ -107,8 +118,8 @@ server-side render, in a script and in a test, unchanged.**
107
118
  @sincpro/criteria the contract with the server: Criteria, Meta, Page, Problem
108
119
  ▲
109
120
  @sincpro/client-framework this package — platform-neutral
110
- │ UseBoundedContext · Feature · ApplicationService · interceptors
111
- │ the process · the context · Tracer, ErrorReporter and ContextStore ports
121
+ │ BoundedContext · Command · Feature · ApplicationService · execution
122
+ │ the process · the context · Tracer, ErrorReporter, Logger and ContextStore ports
112
123
  │ Transport · entity() · client contract · model definitions · ids
113
124
  │ the generator · in-memory doubles for tests
114
125
  ▲
@@ -121,20 +132,29 @@ Dependencies point down only. This package knows nothing above it.
121
132
 
122
133
  ## Where it stands
123
134
 
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.
129
-
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.
135
-
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.
135
+ **The application layer is the backend's.** A bounded context executes class Commands through
136
+ one pipeline — context, span, log lines, outcome, report — with its dependencies injected,
137
+ its ApplicationServices composing Features through `featureBus`, and the backend's error
138
+ handlers: a failure is observed where it happened, then answered or handed on. Screen requests
139
+ and scaffolded applications run through the same pipeline.
140
+
141
+ **Observability is ports, plugged once.** Tracing, error reporting and logging are ports the
142
+ process configures; the console is the default log engine, OpenTelemetry and Sentry optional
143
+ entries that wrap what the application built. The application layer imports no logger: a
144
+ bounded context has its own, and a run hands `this.logger`, bound to its execution; only the
145
+ infrastructure and the adapters have a predefined one.
146
+
147
+ **The client and the generator read the backend's contract.** Types per model, operation and —
148
+ when the contract is exported with them — domain event; a client per wire REST reaches; the
149
+ entities screens use. What is generated is types, never classes: the remote contract describes
150
+ the wire and never reaches the local bus.
151
+
152
+ **Each module is laid out as the backend's.** `domain/`, `adapters/`, `services/`,
153
+ `infrastructure/`, `entrypoint/`, only those a module needs; `domain/` depends only on `domain/`.
154
+
155
+ **Next:** web and mobile adopt it in their own releases; generated readers that validate and
156
+ convert what arrives, when a use needs them; durable delivery, designed apart from
157
+ instrumentation.
138
158
 
139
159
  ## How we decide
140
160