@pattern-stack/codegen 0.17.2 → 0.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +32 -0
- package/README.md +157 -2
- package/consumer-skills/codegen/SKILL.md +32 -0
- package/consumer-skills/entities/SKILL.md +2 -0
- package/dist/{chunk-I6UXRJ3Q.js → chunk-43SBT72G.js} +4 -4
- package/dist/{chunk-T6SCOJF4.js → chunk-7LKAMLV4.js} +4 -4
- package/dist/{chunk-IOQMMH6C.js → chunk-F7KN3U6U.js} +122 -8
- package/dist/chunk-F7KN3U6U.js.map +1 -0
- package/dist/{chunk-CZQUOIDY.js → chunk-J7JMVS2B.js} +4 -4
- package/dist/{chunk-KSTZIULO.js → chunk-K2I6XIK5.js} +4 -4
- package/dist/{chunk-ATVGYF3D.js → chunk-PKDS6QIJ.js} +7 -7
- package/dist/{chunk-KZDHMZ45.js → chunk-SNH35CNA.js} +8 -8
- package/dist/runtime/base-classes/index.js +24 -24
- package/dist/runtime/subsystems/analytics/analytics.module.js +2 -2
- package/dist/runtime/subsystems/analytics/index.js +4 -4
- package/dist/runtime/subsystems/auth/auth.module.js +3 -3
- package/dist/runtime/subsystems/auth/index.js +14 -14
- package/dist/runtime/subsystems/bridge/bridge-delivery.drizzle-backend.js +2 -2
- package/dist/runtime/subsystems/bridge/bridge-outbox-drain-hook.js +1 -1
- package/dist/runtime/subsystems/bridge/bridge.module.js +5 -5
- package/dist/runtime/subsystems/bridge/index.js +13 -13
- package/dist/runtime/subsystems/cache/cache.module.js +1 -1
- package/dist/runtime/subsystems/cache/index.js +3 -3
- package/dist/runtime/subsystems/index.js +94 -94
- package/dist/runtime/subsystems/integration/build-change-source.js +2 -2
- package/dist/runtime/subsystems/integration/index.js +36 -36
- package/dist/runtime/subsystems/integration/integration.module.js +4 -4
- package/dist/src/cli/index.js +1552 -254
- package/dist/src/cli/index.js.map +1 -1
- package/dist/src/index.d.ts +18 -0
- package/dist/src/index.js +12 -12
- package/package.json +1 -1
- package/src/config/locations.mjs +0 -6
- package/src/config/paths.mjs +0 -13
- package/templates/entity/new/prompt.js +12 -88
- package/dist/chunk-IOQMMH6C.js.map +0 -1
- package/templates/entity/new/frontend/_inject-entities-entry.ejs.t +0 -7
- package/templates/entity/new/frontend/_inject-entities-import.ejs.t +0 -7
- package/templates/entity/new/frontend/collections/_ensure-anchor-collections.ejs.t +0 -10
- package/templates/entity/new/frontend/collections/_inject-index.ejs.t +0 -9
- package/templates/entity/new/frontend/collections/_inject-schema-import.ejs.t +0 -9
- package/templates/entity/new/frontend/collections/collection.ejs.t +0 -86
- package/templates/entity/new/frontend/collections/collections-base.ejs.t +0 -35
- package/templates/entity/new/frontend/entity/collection.ejs.t +0 -173
- package/templates/entity/new/frontend/entity/combined.ejs.t +0 -505
- package/templates/entity/new/frontend/entity/fields.ejs.t +0 -105
- package/templates/entity/new/frontend/entity/hooks.ejs.t +0 -74
- package/templates/entity/new/frontend/entity/index.ejs.t +0 -22
- package/templates/entity/new/frontend/entity/mutation-hooks.ejs.t +0 -85
- package/templates/entity/new/frontend/entity/mutations.ejs.t +0 -39
- package/templates/entity/new/frontend/entity/types.ejs.t +0 -60
- package/templates/entity/new/frontend/generated/_inject-index-export.ejs.t +0 -7
- package/templates/entity/new/frontend/generated/_inject-index-import.ejs.t +0 -7
- package/templates/entity/new/frontend/generated/_inject-index-registry.ejs.t +0 -7
- package/templates/entity/new/frontend/store/_inject-collection-import.ejs.t +0 -9
- package/templates/entity/new/frontend/store/_inject-collections.ejs.t +0 -9
- package/templates/entity/new/frontend/store/_inject-entity.ejs.t +0 -9
- package/templates/entity/new/frontend/store/_inject-import.ejs.t +0 -9
- package/templates/entity/new/frontend/store/_inject-lookups.ejs.t +0 -9
- package/templates/entity/new/frontend/store/_inject-resolve.ejs.t +0 -10
- package/templates/entity/new/frontend/store/hooks.ejs.t +0 -73
- package/templates/entity/new/frontend/unified-entity.ejs.t +0 -29
- /package/dist/{chunk-I6UXRJ3Q.js.map → chunk-43SBT72G.js.map} +0 -0
- /package/dist/{chunk-T6SCOJF4.js.map → chunk-7LKAMLV4.js.map} +0 -0
- /package/dist/{chunk-CZQUOIDY.js.map → chunk-J7JMVS2B.js.map} +0 -0
- /package/dist/{chunk-KSTZIULO.js.map → chunk-K2I6XIK5.js.map} +0 -0
- /package/dist/{chunk-ATVGYF3D.js.map → chunk-PKDS6QIJ.js.map} +0 -0
- /package/dist/{chunk-KZDHMZ45.js.map → chunk-SNH35CNA.js.map} +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,38 @@ All notable changes to this project will be documented in this file.
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [0.19.0] — 2026-06-05
|
|
8
|
+
|
|
9
|
+
**Providers catalog emission + planned providers** (ADR-038 follow-on;
|
|
10
|
+
swe-brain consumer-test finding — the Connections surface hand-duplicated
|
|
11
|
+
provider knowledge that `definitions/providers/*.yaml` already owns).
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
|
|
15
|
+
- **Frontend providers catalog (`generated/providers.ts`)** — emitted by the
|
|
16
|
+
frontend whole-set emitter when `definitions/providers/` exists (entity-only
|
|
17
|
+
consumers see no new file; the root barrel gains the export conditionally).
|
|
18
|
+
`PROVIDERS` (flat, slug-sorted) + `PROVIDER_CATALOG` (grouped by
|
|
19
|
+
`display.category` into the ordered `frontend.catalog.categories`). Providers
|
|
20
|
+
are gen-time knowledge — the catalog is emitted, not queried.
|
|
21
|
+
- **`display:` block on the provider schema** (`category`, `blurb`, `hint`) —
|
|
22
|
+
presentation metadata consumed only by the catalog emission; backend
|
|
23
|
+
provider/adapter codegen ignores it.
|
|
24
|
+
- **`status: active | planned` on the provider schema** (default `active`).
|
|
25
|
+
`planned` providers are roadmap stubs: catalog tile only — `auth`/`client`
|
|
26
|
+
optional, surface closed-set + import pre-flight cross-checks skipped (slug
|
|
27
|
+
uniqueness still enforced), and ALL backend emission (provider modules,
|
|
28
|
+
adapters, assemblies) filters them out. Flip to `active` when the
|
|
29
|
+
integration lands.
|
|
30
|
+
- **`frontend.catalog.categories`** in `codegen.config.yaml` — ordered display
|
|
31
|
+
groups (`id`, `name`, `blurb`) the catalog renders.
|
|
32
|
+
|
|
33
|
+
### Changed
|
|
34
|
+
|
|
35
|
+
- `generateProviderModule` / `generateAdapterScaffold` now take
|
|
36
|
+
`ActiveProviderDefinition` (auth/client guaranteed); use the exported
|
|
37
|
+
`isActiveProvider` guard to narrow.
|
|
38
|
+
|
|
7
39
|
## [0.17.2] — 2026-06-04
|
|
8
40
|
|
|
9
41
|
**Shutdown leak fix** (LISTEN-NOTIFY-2; swe-brain dogfood). With
|
package/README.md
CHANGED
|
@@ -138,6 +138,155 @@ modules/{plural}/
|
|
|
138
138
|
use-cases/ FindById, List, declarative queries
|
|
139
139
|
```
|
|
140
140
|
|
|
141
|
+
**Frontend** (`generate.frontend: true`) — see [Frontend generation](#frontend-generation) below.
|
|
142
|
+
|
|
143
|
+
## Frontend generation
|
|
144
|
+
|
|
145
|
+
Gated entirely by `generate.frontend` (default `false`; the scanner sets it
|
|
146
|
+
`true` when it finds an `apps/frontend/` directory). When on, the `entity new`
|
|
147
|
+
post-step (and therefore `gen-all`) renders the **complete frontend tree from the
|
|
148
|
+
full entity set in one pass** — a TypeScript emitter (`src/emitters/frontend/`),
|
|
149
|
+
not hygen templates. Re-running is idempotent: every file is a complete-file
|
|
150
|
+
write with a `@generated` banner, no inject/anchor machinery, no overwrite
|
|
151
|
+
prompts (ADR-038).
|
|
152
|
+
|
|
153
|
+
Output lands under `locations.frontendGenerated` (default
|
|
154
|
+
`apps/frontend/src/generated/`):
|
|
155
|
+
|
|
156
|
+
```
|
|
157
|
+
generated/
|
|
158
|
+
index.ts whole-set barrel (+ version-pairing comment)
|
|
159
|
+
config.ts per-entity sync modes + runtime overrides
|
|
160
|
+
query-client.ts shared TanStack QueryClient
|
|
161
|
+
api/<entity>.ts REST client → the generated NestJS controllers
|
|
162
|
+
collections/<entity>.ts createCollection, branched on the entity's sync mode
|
|
163
|
+
entities/<entity>.ts createEntityHooks({ collection, api }) wiring
|
|
164
|
+
store/index.ts createStore over the full set (+ resolvers, lookups)
|
|
165
|
+
fields/<entity>.ts field metadata (FieldMeta, <entity>Fields)
|
|
166
|
+
providers.ts providers catalog — only when definitions/providers/ exists
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
Entity types and Zod schemas are **imported** from `locations.dbEntities`
|
|
170
|
+
(default `@repo/db/entities`), not re-emitted. The emitter imports the plain
|
|
171
|
+
class name — `import type { <Class> } from '<dbEntities>/<name>'` — so
|
|
172
|
+
`dbEntities` is the contract: it MUST export the entity type under its plain
|
|
173
|
+
`<Class>` name (e.g. `Contact`, not `ContactEntity`). If your db package only
|
|
174
|
+
exports `<Class>Entity`, that is the one knob to change in the emitter.
|
|
175
|
+
|
|
176
|
+
The hook/mutation/store/provider logic is consumed from
|
|
177
|
+
`@pattern-stack/frontend-patterns` (`createEntityHooks`, `createStore`) — the
|
|
178
|
+
generated files are thin wiring. **The consumer's frontend installs that package
|
|
179
|
+
plus the paired TanStack libraries.** `project init` adds the version-pairing
|
|
180
|
+
deps to `apps/frontend/package.json` when `generate.frontend: true` (idempotent
|
|
181
|
+
merge — only missing keys added, existing version ranges preserved); when no
|
|
182
|
+
frontend package.json exists it prints the list to install. `@pattern-stack/codegen`
|
|
183
|
+
itself gains no runtime dependency.
|
|
184
|
+
|
|
185
|
+
| Package | Range |
|
|
186
|
+
|---|---|
|
|
187
|
+
| `@pattern-stack/frontend-patterns` | `^0.2.0-alpha.18` |
|
|
188
|
+
| `@tanstack/react-db` | `^0.1.55` |
|
|
189
|
+
| `@tanstack/electric-db-collection` | `^0.2.11` |
|
|
190
|
+
| `@tanstack/query-db-collection` | `^1.0.6` |
|
|
191
|
+
| `@tanstack/react-query` | `^5.0.0` |
|
|
192
|
+
|
|
193
|
+
### `frontend:` config block
|
|
194
|
+
|
|
195
|
+
All knobs are inert unless `generate.frontend: true`. Defaults shown; the block
|
|
196
|
+
is optional (fully defaulted when absent):
|
|
197
|
+
|
|
198
|
+
```yaml
|
|
199
|
+
frontend:
|
|
200
|
+
auth:
|
|
201
|
+
function: getAuthorizationHeader # auth-header fn; null DISABLES the header
|
|
202
|
+
parsers: # Electric column-type → parser fn source
|
|
203
|
+
timestamptz: '(date: string) => new Date(date)'
|
|
204
|
+
sync:
|
|
205
|
+
mode: electric # global default sync mode (api | electric)
|
|
206
|
+
shapeUrl: /v1/shape # Electric shape base path
|
|
207
|
+
useTableParam: true # emit `params: { table }` shape-URL form
|
|
208
|
+
columnMapper: snakeCamelMapper # Electric column mapper fn; null to omit
|
|
209
|
+
columnMapperNeedsCall: true # call the mapper (fn()) vs reference (fn)
|
|
210
|
+
apiBaseUrlImport: null # when set, import API_BASE_URL from it as baseURL
|
|
211
|
+
apiUrl: /api # REST base path when no apiBaseUrlImport
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
`null`-disables convention: an **absent** `auth.function` defaults to
|
|
215
|
+
`getAuthorizationHeader`; an **explicit `null`** disables it entirely (no header
|
|
216
|
+
lines emitted). Likewise `sync.columnMapper: null` omits the Electric mapper.
|
|
217
|
+
|
|
218
|
+
### Per-entity sync mode (`entity.sync`)
|
|
219
|
+
|
|
220
|
+
Each entity may override the global `frontend.sync.mode` inside its `entity:`
|
|
221
|
+
block (sibling to `surface:`/`context:`):
|
|
222
|
+
|
|
223
|
+
```yaml
|
|
224
|
+
entity:
|
|
225
|
+
name: contact
|
|
226
|
+
plural: contacts
|
|
227
|
+
table: contacts
|
|
228
|
+
sync: api # api | electric — overrides frontend.sync.mode for this entity
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
`api` wires `queryCollectionOptions` (REST via TanStack Query); `electric` wires
|
|
232
|
+
`electricCollectionOptions` (real-time shape sync). Absent → the global default.
|
|
233
|
+
`offline` (Electric + Dexie) is deferred — the schema rejects it.
|
|
234
|
+
|
|
235
|
+
Cross-entity FK names (file, plural, class, collection var) are resolved against
|
|
236
|
+
the **target entity's own YAML** via the registry — never re-pluralized from a
|
|
237
|
+
string at emit time (so an explicit `plural:` like `person`→`persons` is honored
|
|
238
|
+
by every consumer).
|
|
239
|
+
|
|
240
|
+
### Providers catalog (`providers.ts`)
|
|
241
|
+
|
|
242
|
+
Providers are gen-time knowledge (`definitions/providers/<slug>.yaml`,
|
|
243
|
+
RFC-0001) — the provider set changes only when code deploys — so the frontend
|
|
244
|
+
catalog is **emitted, not queried**. When the project has provider definitions,
|
|
245
|
+
the emitter renders `generated/providers.ts`:
|
|
246
|
+
|
|
247
|
+
- `PROVIDERS` — every provider, flat (active + planned), slug-sorted:
|
|
248
|
+
`{ provider, name, planned, surfaces, blurb?, hint? }`. Join live rows on
|
|
249
|
+
`provider` (the canonical slug, e.g. `Connection.provider`).
|
|
250
|
+
- `PROVIDER_CATALOG` — grouped into `frontend.catalog.categories` (config
|
|
251
|
+
order) via each provider's `display.category`. Uncategorized providers
|
|
252
|
+
appear only in `PROVIDERS`.
|
|
253
|
+
|
|
254
|
+
Two provider-YAML additions feed it:
|
|
255
|
+
|
|
256
|
+
```yaml
|
|
257
|
+
# definitions/providers/google.yaml (active — full definition)
|
|
258
|
+
slug: google
|
|
259
|
+
display_name: Google Workspace
|
|
260
|
+
display:
|
|
261
|
+
category: google-workspace # joins frontend.catalog.categories[].id
|
|
262
|
+
hint: connect # optional sub-line on an unconnected tile
|
|
263
|
+
surfaces: [calendar, mail, transcript]
|
|
264
|
+
auth: { ... } # required for active providers
|
|
265
|
+
client: { ... }
|
|
266
|
+
|
|
267
|
+
# definitions/providers/github.yaml (planned — roadmap stub)
|
|
268
|
+
slug: github
|
|
269
|
+
display_name: GitHub
|
|
270
|
+
status: planned # catalog tile only; NO backend emission,
|
|
271
|
+
display: # no auth/client required, surface + import
|
|
272
|
+
category: source-control # cross-checks skipped
|
|
273
|
+
surfaces: [source_control]
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
```yaml
|
|
277
|
+
# codegen.config.yaml — the ordered display groups
|
|
278
|
+
frontend:
|
|
279
|
+
catalog:
|
|
280
|
+
categories:
|
|
281
|
+
- id: source-control
|
|
282
|
+
name: Source Control & Issues
|
|
283
|
+
blurb: Repositories, pull requests, issues, and project planning
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
When the integration for a `planned` provider lands, flip it to `status:
|
|
287
|
+
active` (or drop the key) and add `auth`/`client` — the definitions tree is
|
|
288
|
+
the integration roadmap.
|
|
289
|
+
|
|
141
290
|
## Integration Codegen (provider/adapter + assembly + read primitive)
|
|
142
291
|
|
|
143
292
|
When an entity carries a `surface:` tag and `definitions/providers/*.yaml` exist,
|
|
@@ -261,9 +410,14 @@ naming:
|
|
|
261
410
|
terminology:
|
|
262
411
|
command: use-case
|
|
263
412
|
query: use-case
|
|
413
|
+
|
|
414
|
+
# frontend: # inert unless generate.frontend: true
|
|
415
|
+
# ... # see "Frontend generation" above for the full block
|
|
264
416
|
```
|
|
265
417
|
|
|
266
|
-
|
|
418
|
+
The `frontend:` block (auth, parsers, sync) is documented under
|
|
419
|
+
[Frontend generation](#frontend-generation). Auto-detect your project's
|
|
420
|
+
conventions with `codegen project scan`.
|
|
267
421
|
|
|
268
422
|
## Using in Your Project
|
|
269
423
|
|
|
@@ -276,6 +430,7 @@ See [docs/GETTING-STARTED.md](docs/GETTING-STARTED.md) for a walkthrough of enti
|
|
|
276
430
|
```
|
|
277
431
|
src/ Generator source code
|
|
278
432
|
cli/ Clipanion CLI (noun-verb architecture)
|
|
433
|
+
emitters/ TypeScript emitters (integration, frontend — ADR-038)
|
|
279
434
|
analyzer/ Graph building, consistency checking
|
|
280
435
|
parser/ YAML loading, cross-reference resolution
|
|
281
436
|
scanner/ Project pattern detection
|
|
@@ -287,7 +442,7 @@ src/ Generator source code
|
|
|
287
442
|
runtime/ Code shipped into consumer projects
|
|
288
443
|
base-classes/ BaseRepository, BaseService, pattern bases
|
|
289
444
|
subsystems/ Events, Jobs, Cache, Storage
|
|
290
|
-
templates/ Hygen EJS templates
|
|
445
|
+
templates/ Hygen EJS templates (backend pipelines)
|
|
291
446
|
test/ Baseline snapshots, scaffold integration, smoke test
|
|
292
447
|
docs/ ADRs, consumer setup, getting started
|
|
293
448
|
```
|
|
@@ -55,6 +55,38 @@ generation run the two barrels are rewritten and you wire them into
|
|
|
55
55
|
| React to an event with a durable async job (the event-to-job bridge) | the `bridge` skill |
|
|
56
56
|
| Pull/push data from an external system (`IChangeSource` / `IIntegrationSink`) | the `integration` skill |
|
|
57
57
|
|
|
58
|
+
## Frontend generation (`generate.frontend: true`)
|
|
59
|
+
|
|
60
|
+
When on, `entity new` (and `--all`) ends with a **whole-set frontend emitter**
|
|
61
|
+
(ADR-038): the complete data layer is rendered into
|
|
62
|
+
`locations.frontendGenerated` (default `apps/frontend/src/generated/`) from the
|
|
63
|
+
full entity set — REST api client, TanStack DB collections (per-entity
|
|
64
|
+
`sync: api | electric`), `createEntityHooks` wiring, field metadata, a
|
|
65
|
+
plural-keyed `createStore`, `config.ts`, `query-client.ts`, root barrel. Every
|
|
66
|
+
file is a complete write with a `@generated` banner; re-runs are byte-identical.
|
|
67
|
+
|
|
68
|
+
Non-obvious bits:
|
|
69
|
+
|
|
70
|
+
- **The dbEntities contract**: generated files import the plain `<Class>` type
|
|
71
|
+
AND a `<camel>Schema` Zod schema from `locations.dbEntities` per entity. If
|
|
72
|
+
the backend doesn't emit such a package (clean-lite-ps doesn't), the consumer
|
|
73
|
+
provides a shim barrel re-exporting each module's Output DTO.
|
|
74
|
+
- **The consumer mounts two providers** in the app root, both from generated
|
|
75
|
+
code: `QueryClientProvider(queryClient)` ▸ `EntityStoreProvider(store)`
|
|
76
|
+
(`EntityStoreProvider` from `@pattern-stack/frontend-patterns`).
|
|
77
|
+
- **Version pairing**: the emitted imports require
|
|
78
|
+
`@pattern-stack/frontend-patterns` + four TanStack packages — the exact
|
|
79
|
+
ranges are listed in the generated `index.ts` header comment.
|
|
80
|
+
- **Path aliases are assumed**: default imports use `@/…` (and your
|
|
81
|
+
`locations.*.import` values) — wire tsconfig `paths` + the bundler alias.
|
|
82
|
+
- **Providers catalog**: when `definitions/providers/` exists, the emitter also
|
|
83
|
+
renders `providers.ts` — `PROVIDERS` (flat) + `PROVIDER_CATALOG` (grouped by
|
|
84
|
+
each provider's `display.category` into `frontend.catalog.categories`).
|
|
85
|
+
Providers are gen-time knowledge: the catalog is emitted, never queried, and
|
|
86
|
+
never hand-duplicated in the frontend. `status: planned` provider YAMLs are
|
|
87
|
+
roadmap stubs — catalog tile only, no auth/client needed, skipped by all
|
|
88
|
+
backend emission.
|
|
89
|
+
|
|
58
90
|
## CLI quick reference
|
|
59
91
|
|
|
60
92
|
```bash
|
|
@@ -48,6 +48,8 @@ module — plus an entry in the `GENERATED_MODULES` and schema barrels.
|
|
|
48
48
|
entity:
|
|
49
49
|
name: account # singular snake_case
|
|
50
50
|
pattern: Synced # Base | Synced | Activity | Metadata | Knowledge (or app-defined)
|
|
51
|
+
# sync: api # frontend collection mode (api | electric) — overrides
|
|
52
|
+
# # frontend.sync.mode; only meaningful with generate.frontend: true
|
|
51
53
|
|
|
52
54
|
fields:
|
|
53
55
|
name:
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import {
|
|
2
|
-
WebhookChangeSource
|
|
3
|
-
} from "./chunk-TIZXQU26.js";
|
|
4
1
|
import {
|
|
5
2
|
PollChangeSource
|
|
6
3
|
} from "./chunk-4MF3HKJA.js";
|
|
4
|
+
import {
|
|
5
|
+
WebhookChangeSource
|
|
6
|
+
} from "./chunk-TIZXQU26.js";
|
|
7
7
|
|
|
8
8
|
// runtime/subsystems/integration/build-change-source.ts
|
|
9
9
|
function buildChangeSource(cfg, fetch, middlewares = []) {
|
|
@@ -26,4 +26,4 @@ function buildChangeSource(cfg, fetch, middlewares = []) {
|
|
|
26
26
|
export {
|
|
27
27
|
buildChangeSource
|
|
28
28
|
};
|
|
29
|
-
//# sourceMappingURL=chunk-
|
|
29
|
+
//# sourceMappingURL=chunk-43SBT72G.js.map
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import {
|
|
2
2
|
EnvEncryptionKey
|
|
3
3
|
} from "./chunk-IP4OO26U.js";
|
|
4
|
-
import {
|
|
5
|
-
AuthController
|
|
6
|
-
} from "./chunk-SZVPIHWE.js";
|
|
7
4
|
import {
|
|
8
5
|
DrizzleOAuthStateStore
|
|
9
6
|
} from "./chunk-N5OTOWTP.js";
|
|
10
7
|
import {
|
|
11
8
|
MemoryOAuthStateStore
|
|
12
9
|
} from "./chunk-QLTJSCE6.js";
|
|
10
|
+
import {
|
|
11
|
+
AuthController
|
|
12
|
+
} from "./chunk-SZVPIHWE.js";
|
|
13
13
|
import {
|
|
14
14
|
AUTH_OPTIONS,
|
|
15
15
|
ENCRYPTION_KEY,
|
|
@@ -89,4 +89,4 @@ AuthModule = __decorateClass([
|
|
|
89
89
|
export {
|
|
90
90
|
AuthModule
|
|
91
91
|
};
|
|
92
|
-
//# sourceMappingURL=chunk-
|
|
92
|
+
//# sourceMappingURL=chunk-7LKAMLV4.js.map
|
|
@@ -349,7 +349,14 @@ var EntityConfigSchema = z.object({
|
|
|
349
349
|
context: z.string().regex(
|
|
350
350
|
/^[a-z][a-z0-9_]*$/,
|
|
351
351
|
"context must be lowercase snake_case (e.g. 'integration')"
|
|
352
|
-
).optional()
|
|
352
|
+
).optional(),
|
|
353
|
+
// ADR-038: per-entity frontend sync mode. Overrides global frontend.sync.mode.
|
|
354
|
+
// 'api' → queryCollectionOptions (REST via TanStack Query)
|
|
355
|
+
// 'electric' → electricCollectionOptions (real-time shape sync)
|
|
356
|
+
// 'offline' (Electric + Dexie) is deferred — see
|
|
357
|
+
// docs/specs/2026-06-04-frontend-pipeline-rebuild.md OQ-6.
|
|
358
|
+
// Sibling to `surface:`/`context:`; lives inside the `entity:` block.
|
|
359
|
+
sync: z.enum(["api", "electric"]).optional()
|
|
353
360
|
}).strict().refine((d) => !(d.pattern && d.patterns), {
|
|
354
361
|
message: "'pattern' and 'patterns' are mutually exclusive"
|
|
355
362
|
});
|
|
@@ -1004,6 +1011,12 @@ var ClientSchema = z5.object({
|
|
|
1004
1011
|
class: ImportRefSchema,
|
|
1005
1012
|
base_url: z5.string().url("client.base_url must be an absolute URL")
|
|
1006
1013
|
}).strict();
|
|
1014
|
+
var DisplaySchema = z5.object({
|
|
1015
|
+
category: z5.string().optional(),
|
|
1016
|
+
blurb: z5.string().optional(),
|
|
1017
|
+
// Sub-line shown on an unconnected ("available") tile.
|
|
1018
|
+
hint: z5.string().optional()
|
|
1019
|
+
}).strict();
|
|
1007
1020
|
var ProviderDefinitionSchema = z5.object({
|
|
1008
1021
|
// Provider id — the canonical string used as detection: keys, audit rows,
|
|
1009
1022
|
// subscription rows. kebab/lower; unique across definitions/providers/
|
|
@@ -1013,19 +1026,49 @@ var ProviderDefinitionSchema = z5.object({
|
|
|
1013
1026
|
"slug must be kebab-case lower (e.g. 'google', 'hubspot')"
|
|
1014
1027
|
),
|
|
1015
1028
|
display_name: z5.string().optional(),
|
|
1016
|
-
|
|
1017
|
-
client
|
|
1029
|
+
// Lifecycle: 'active' providers drive backend provider/adapter emission
|
|
1030
|
+
// and require auth + client. 'planned' providers are roadmap stubs — they
|
|
1031
|
+
// appear in the frontend catalog (as unconnectable tiles) but are skipped
|
|
1032
|
+
// by all backend emission and by the surface/import cross-checks, so a
|
|
1033
|
+
// stub can exist before its surface has entities or its strategy/client
|
|
1034
|
+
// are written.
|
|
1035
|
+
status: z5.enum(["active", "planned"]).default("active"),
|
|
1036
|
+
// Frontend catalog presentation (see DisplaySchema).
|
|
1037
|
+
display: DisplaySchema.optional(),
|
|
1038
|
+
// Required iff status === 'active'; see superRefine below.
|
|
1039
|
+
auth: AuthSchema.optional(),
|
|
1040
|
+
client: ClientSchema.optional(),
|
|
1018
1041
|
// Surfaces this provider serves (ADR-0006: surfaces span contexts — one
|
|
1019
1042
|
// Google OAuth feeds calendar+mail+transcript). Each must reference a real
|
|
1020
1043
|
// `surface:` declared on some entity; that cross-check is in
|
|
1021
|
-
// validate-providers.ts. Non-empty enforced here.
|
|
1044
|
+
// validate-providers.ts (skipped for 'planned'). Non-empty enforced here.
|
|
1022
1045
|
surfaces: z5.array(z5.string()).min(1, "surfaces must list at least one surface"),
|
|
1023
1046
|
// Optional auth lifecycle hints consumed by provider-module emission (D2).
|
|
1024
1047
|
// `refresh_behavior` is left as a free string in D1 — its domain firms up
|
|
1025
1048
|
// when D2 consumes it; carrying it now keeps the YAML lossless.
|
|
1026
1049
|
token_lifetime: z5.number().int().positive().optional(),
|
|
1027
1050
|
refresh_behavior: z5.string().optional()
|
|
1028
|
-
}).strict()
|
|
1051
|
+
}).strict().superRefine((def, ctx) => {
|
|
1052
|
+
if (def.status === "active") {
|
|
1053
|
+
if (!def.auth) {
|
|
1054
|
+
ctx.addIssue({
|
|
1055
|
+
code: z5.ZodIssueCode.custom,
|
|
1056
|
+
message: "auth is required when status is 'active'",
|
|
1057
|
+
path: ["auth"]
|
|
1058
|
+
});
|
|
1059
|
+
}
|
|
1060
|
+
if (!def.client) {
|
|
1061
|
+
ctx.addIssue({
|
|
1062
|
+
code: z5.ZodIssueCode.custom,
|
|
1063
|
+
message: "client is required when status is 'active'",
|
|
1064
|
+
path: ["client"]
|
|
1065
|
+
});
|
|
1066
|
+
}
|
|
1067
|
+
}
|
|
1068
|
+
});
|
|
1069
|
+
function isActiveProvider(def) {
|
|
1070
|
+
return def.status !== "planned";
|
|
1071
|
+
}
|
|
1029
1072
|
|
|
1030
1073
|
// src/utils/yaml-loader.ts
|
|
1031
1074
|
function loadEntityFromYaml(filePath) {
|
|
@@ -1320,6 +1363,7 @@ function transformToEntity(result) {
|
|
|
1320
1363
|
patterns: definition.entity.patterns,
|
|
1321
1364
|
patternConfig: definition.entity.config,
|
|
1322
1365
|
scopeable: definition.entity.scopeable ?? false,
|
|
1366
|
+
expose: definition.entity.expose ?? ["repository", "rest", "trpc"],
|
|
1323
1367
|
folderStructure: definition.entity.folder_structure ?? "nested",
|
|
1324
1368
|
fields: /* @__PURE__ */ new Map(),
|
|
1325
1369
|
relationships: /* @__PURE__ */ new Map(),
|
|
@@ -1664,9 +1708,75 @@ function resolveRelationshipReferences(relationshipDefs, entities) {
|
|
|
1664
1708
|
return issues;
|
|
1665
1709
|
}
|
|
1666
1710
|
|
|
1711
|
+
// src/parser/entity-registry.ts
|
|
1712
|
+
import { resolve as resolve3 } from "path";
|
|
1713
|
+
var camelCase = (s) => s.replace(/_([a-z])/g, (_, c) => c.toUpperCase());
|
|
1714
|
+
var pascalCase = (s) => {
|
|
1715
|
+
const camel = camelCase(s);
|
|
1716
|
+
return camel.charAt(0).toUpperCase() + camel.slice(1);
|
|
1717
|
+
};
|
|
1718
|
+
function loadErrorToIssue2(error) {
|
|
1719
|
+
const issues = [
|
|
1720
|
+
{
|
|
1721
|
+
severity: "error",
|
|
1722
|
+
type: "parse_error",
|
|
1723
|
+
message: error.error,
|
|
1724
|
+
path: error.filePath
|
|
1725
|
+
}
|
|
1726
|
+
];
|
|
1727
|
+
if (error.details) {
|
|
1728
|
+
for (const detail of error.details) {
|
|
1729
|
+
issues.push({
|
|
1730
|
+
severity: "error",
|
|
1731
|
+
type: "schema_error",
|
|
1732
|
+
message: detail,
|
|
1733
|
+
path: error.filePath
|
|
1734
|
+
});
|
|
1735
|
+
}
|
|
1736
|
+
}
|
|
1737
|
+
return issues;
|
|
1738
|
+
}
|
|
1739
|
+
function loadEntityRegistry(entitiesDir) {
|
|
1740
|
+
const registry = /* @__PURE__ */ new Map();
|
|
1741
|
+
const issues = [];
|
|
1742
|
+
const resolvedDir = resolve3(entitiesDir);
|
|
1743
|
+
let files;
|
|
1744
|
+
try {
|
|
1745
|
+
files = findYamlFiles(resolvedDir);
|
|
1746
|
+
} catch {
|
|
1747
|
+
issues.push({
|
|
1748
|
+
severity: "error",
|
|
1749
|
+
type: "parse_error",
|
|
1750
|
+
message: `Failed to read directory: ${resolvedDir}`,
|
|
1751
|
+
path: resolvedDir
|
|
1752
|
+
});
|
|
1753
|
+
return { registry, issues };
|
|
1754
|
+
}
|
|
1755
|
+
for (const filePath of files) {
|
|
1756
|
+
const result = loadEntityFromYaml(filePath);
|
|
1757
|
+
if (!result.success) {
|
|
1758
|
+
issues.push(...loadErrorToIssue2(result));
|
|
1759
|
+
continue;
|
|
1760
|
+
}
|
|
1761
|
+
const { entity } = result.definition;
|
|
1762
|
+
registry.set(entity.name, {
|
|
1763
|
+
name: entity.name,
|
|
1764
|
+
plural: entity.plural,
|
|
1765
|
+
// authoritative — never derived
|
|
1766
|
+
table: entity.table,
|
|
1767
|
+
className: pascalCase(entity.name),
|
|
1768
|
+
classNamePlural: pascalCase(entity.plural),
|
|
1769
|
+
camelName: camelCase(entity.name),
|
|
1770
|
+
pluralCamelName: camelCase(entity.plural),
|
|
1771
|
+
sync: entity.sync ?? null
|
|
1772
|
+
});
|
|
1773
|
+
}
|
|
1774
|
+
return { registry, issues };
|
|
1775
|
+
}
|
|
1776
|
+
|
|
1667
1777
|
// src/parser/validate-providers.ts
|
|
1668
1778
|
import { existsSync as existsSync2, readFileSync as readFileSync2, statSync } from "fs";
|
|
1669
|
-
import { isAbsolute, join as join2, resolve as
|
|
1779
|
+
import { isAbsolute, join as join2, resolve as resolve4 } from "path";
|
|
1670
1780
|
import ts from "typescript";
|
|
1671
1781
|
function collectEntitySurfaces(entities) {
|
|
1672
1782
|
const surfaces = /* @__PURE__ */ new Set();
|
|
@@ -1698,7 +1808,7 @@ function resolveModuleFile(importPath, opts) {
|
|
|
1698
1808
|
}
|
|
1699
1809
|
}
|
|
1700
1810
|
if (base === null) {
|
|
1701
|
-
base = isAbsolute(importPath) ? importPath :
|
|
1811
|
+
base = isAbsolute(importPath) ? importPath : resolve4(opts.sourceRoot, importPath);
|
|
1702
1812
|
}
|
|
1703
1813
|
const candidates = [
|
|
1704
1814
|
base,
|
|
@@ -1769,6 +1879,7 @@ function validateProviders(providers, opts) {
|
|
|
1769
1879
|
}
|
|
1770
1880
|
for (const { definition, filePath } of providers) {
|
|
1771
1881
|
const { slug } = definition;
|
|
1882
|
+
if (definition.status === "planned") continue;
|
|
1772
1883
|
for (const surface of definition.surfaces) {
|
|
1773
1884
|
if (!knownSurfaces.has(surface)) {
|
|
1774
1885
|
const known = [...knownSurfaces].sort().join(", ") || "(none declared)";
|
|
@@ -1790,6 +1901,7 @@ function validateProviders(providers, opts) {
|
|
|
1790
1901
|
sourceRoot: opts.sourceRoot,
|
|
1791
1902
|
aliases: opts.aliases
|
|
1792
1903
|
};
|
|
1904
|
+
if (!isActiveProvider(definition)) continue;
|
|
1793
1905
|
const refs = [
|
|
1794
1906
|
{ field: "auth.strategy", ref: definition.auth.strategy },
|
|
1795
1907
|
{ field: "client.class", ref: definition.client.class }
|
|
@@ -3990,6 +4102,7 @@ export {
|
|
|
3990
4102
|
validateJunctionDefinition,
|
|
3991
4103
|
safeValidateJunctionDefinition,
|
|
3992
4104
|
parseImportRef,
|
|
4105
|
+
isActiveProvider,
|
|
3993
4106
|
loadEntityFromYaml,
|
|
3994
4107
|
loadEntitiesFromYaml,
|
|
3995
4108
|
loadRelationshipFromYaml,
|
|
@@ -3999,6 +4112,7 @@ export {
|
|
|
3999
4112
|
detectYamlType,
|
|
4000
4113
|
loadEntities,
|
|
4001
4114
|
loadRelationships,
|
|
4115
|
+
loadEntityRegistry,
|
|
4002
4116
|
collectEntitySurfaces,
|
|
4003
4117
|
validateProviders,
|
|
4004
4118
|
buildDomainGraph,
|
|
@@ -4049,4 +4163,4 @@ export {
|
|
|
4049
4163
|
analyzeDomain,
|
|
4050
4164
|
validateEntities
|
|
4051
4165
|
};
|
|
4052
|
-
//# sourceMappingURL=chunk-
|
|
4166
|
+
//# sourceMappingURL=chunk-F7KN3U6U.js.map
|