@sun-asterisk/sungen 3.2.10 → 3.2.12-beta.1
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/dist/capabilities/discover.d.ts.map +1 -1
- package/dist/capabilities/discover.js +5 -2
- package/dist/capabilities/discover.js.map +1 -1
- package/dist/cli/commands/capability.d.ts.map +1 -1
- package/dist/cli/commands/capability.js +44 -2
- package/dist/cli/commands/capability.js.map +1 -1
- package/dist/harness/capability.d.ts +1 -0
- package/dist/harness/capability.d.ts.map +1 -1
- package/dist/harness/capability.js.map +1 -1
- package/dist/harness/catalog/drivers.yaml +2 -1
- package/dist/harness/query-catalog.d.ts +32 -2
- package/dist/harness/query-catalog.d.ts.map +1 -1
- package/dist/harness/query-catalog.js +0 -0
- package/dist/harness/query-catalog.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/orchestrator/templates/ai-src/commands/create-data-test.md +99 -0
- package/dist/orchestrator/templates/ai-src/commands/create-test.md +3 -0
- package/dist/orchestrator/templates/ai-src/commands/run-test.md +1 -0
- package/dist/orchestrator/templates/ai-src/config/claude.md +3 -1
- package/dist/orchestrator/templates/ai-src/config/copilot.md +3 -1
- package/dist/orchestrator/templates/ai-src/skills/sungen-data-factory/SKILL.md +116 -0
- package/dist/orchestrator/templates/ai-src/skills/sungen-gherkin-syntax/SKILL.md +15 -5
- package/dist/orchestrator/templates/ai-src/skills/sungen-tc-generation/SKILL.md +1 -0
- package/dist/orchestrator/templates/specs-db.d.ts +201 -11
- package/dist/orchestrator/templates/specs-db.d.ts.map +1 -1
- package/dist/orchestrator/templates/specs-db.js +440 -91
- package/dist/orchestrator/templates/specs-db.js.map +1 -1
- package/dist/orchestrator/templates/specs-db.ts +463 -79
- package/dist/orchestrator/templates/specs-test-data.ts +39 -3
- package/package.json +8 -4
- package/src/capabilities/discover.ts +5 -2
- package/src/cli/commands/capability.ts +40 -2
- package/src/harness/capability.ts +1 -0
- package/src/harness/catalog/drivers.yaml +2 -1
- package/src/harness/query-catalog.ts +0 -0
- package/src/index.ts +2 -2
- package/src/orchestrator/templates/ai-src/commands/create-data-test.md +99 -0
- package/src/orchestrator/templates/ai-src/commands/create-test.md +3 -0
- package/src/orchestrator/templates/ai-src/commands/run-test.md +1 -0
- package/src/orchestrator/templates/ai-src/config/claude.md +3 -1
- package/src/orchestrator/templates/ai-src/config/copilot.md +3 -1
- package/src/orchestrator/templates/ai-src/skills/sungen-data-factory/SKILL.md +116 -0
- package/src/orchestrator/templates/ai-src/skills/sungen-gherkin-syntax/SKILL.md +15 -5
- package/src/orchestrator/templates/ai-src/skills/sungen-tc-generation/SKILL.md +1 -0
- package/src/orchestrator/templates/specs-db.ts +463 -79
- package/src/orchestrator/templates/specs-test-data.ts +39 -3
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: sungen-data-factory
|
|
3
|
+
description: 'Use when create-data-test (or create-test) needs standardized TEST DATA — valid/boundary/invalid values for input fields with CHK-* traceability. Drives the Data Factory catalog + the 4-source method. Invoke after reading spec.md, when authoring the field-map or filling test-data.'
|
|
4
|
+
user-invocable: false
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## What this skill does
|
|
8
|
+
|
|
9
|
+
Turn a screen/flow's fields into **standardized test data** — not guessed values. You author a
|
|
10
|
+
**field-map**; the deterministic `sungen data gen` expands it from the Data Factory **catalog**
|
|
11
|
+
(field-type + security + technique library) into valid / boundary / invalid values, each carrying a
|
|
12
|
+
`CHK-*` id that traces to the spec. Your judgment goes into *reading the spec correctly* and
|
|
13
|
+
*answering blind spots* — never into inventing values the catalog or the QA should own.
|
|
14
|
+
|
|
15
|
+
Data Factory is **bundled with core** (runtime-free) — no `capability add` needed; the `sungen data …`
|
|
16
|
+
CLI is available out of the box.
|
|
17
|
+
|
|
18
|
+
## The 4-source method (phuong-phap-tao-test-data.md)
|
|
19
|
+
|
|
20
|
+
Never invent values from memory. Cross-reference four sources — each covers the previous one's gap:
|
|
21
|
+
|
|
22
|
+
1. **Spec validate rules** — required, format, min/max, business rules, and the **error/message code**
|
|
23
|
+
per rule. Defines the legal/illegal boundary. (Says the *law*, not every way to *break* it.)
|
|
24
|
+
2. **Field-type catalog** — the accumulated "how this type breaks" knowledge (email/password/phone/
|
|
25
|
+
number/…). This is the Data Factory `common/` — it fills the "spec doesn't list every break" gap.
|
|
26
|
+
3. **Design technique** — Boundary Value Analysis / Equivalence Partitioning / Decision Table / State
|
|
27
|
+
Transition — reduces the rule + candidates to a *minimal-but-sufficient* representative set.
|
|
28
|
+
4. **DB mapping** — unique constraints, cross-entity dependency, real backend limits → precondition
|
|
29
|
+
data, not just a single input value.
|
|
30
|
+
|
|
31
|
+
Then classify every value into **Valid / Boundary / Invalid** (each invalid tagged with its error
|
|
32
|
+
code) and give it a `CHK-*` id for traceability.
|
|
33
|
+
|
|
34
|
+
## The field-map — `qa/data-factory/<name>.fields.yaml`
|
|
35
|
+
|
|
36
|
+
Your main artifact. One entry per input field; the generator expands it:
|
|
37
|
+
|
|
38
|
+
```yaml
|
|
39
|
+
screen: login # or flow: <name>
|
|
40
|
+
fields:
|
|
41
|
+
- name: email
|
|
42
|
+
type: email # a catalog field-type (see list below)
|
|
43
|
+
required: true
|
|
44
|
+
constraints: { maxLocal: 64, maxTotal: 254 } # the project's REAL limits from spec
|
|
45
|
+
errorMap: { M13: E1042, M02: E1001 } # catalog placeholder code → project's real code
|
|
46
|
+
- name: password
|
|
47
|
+
type: password
|
|
48
|
+
constraints: { minLength: 8, maxLength: 32 }
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
**Catalog field-types** (P1): `email` · `password` · `phone` · `string` · `number-integer` ·
|
|
52
|
+
`number-float` · `currency` · `postcode` · `katakana` (JP). Security payload banks auto-append to a
|
|
53
|
+
field's invalid group via the type's `uses_security` (`sqli`, `xss`, `idor`, `broken-auth`, `upload`,
|
|
54
|
+
`input-validation`).
|
|
55
|
+
|
|
56
|
+
Rules:
|
|
57
|
+
- Pick the closest `type`; put the project's real numbers in `constraints` (they override the
|
|
58
|
+
catalog defaults).
|
|
59
|
+
- Map every `errorRef` placeholder (`M13`, `M39`, …) to the spec's real code via `errorMap`. Leave
|
|
60
|
+
one unmapped **only** if the spec has none — the lint flags it as an open point to confirm.
|
|
61
|
+
- **Complex forms:** a **group** field takes nested **`fields:`** (recursive; no `type` on the group → renders a nested object like `address.city`). A field whose rule depends on another takes **`dependsOn: { field, value }`** — recorded as a `_depends_on` note (the generator doesn't resolve the runtime condition; weave it into the scenario/`@cases`).
|
|
62
|
+
- Set **`unique: true`** on a field that must be fresh every run (register email/username, anything
|
|
63
|
+
that would clash with a record a previous run left behind). Its valid value then carries a runtime
|
|
64
|
+
`{{$timestamp}}` (email → `user+{{$timestamp}}@…`) so each run is unique — no cross-run cache
|
|
65
|
+
(which would make tests order-dependent/flaky). Prefer this over caching prior-run values. For
|
|
66
|
+
reusing a value produced earlier **within the same run** (register → then log in with it), bind it
|
|
67
|
+
via the scenario (`@dataFactory` recipe / `testData.bind`), not a cross-run cache.
|
|
68
|
+
|
|
69
|
+
## No input fields → nothing to generate
|
|
70
|
+
|
|
71
|
+
Data Factory standardizes **field-level input data**. If a unit has no user-input fields or
|
|
72
|
+
parameters — a pure navigation, listing, or capture-compare flow — there is nothing to generate.
|
|
73
|
+
Report *"no input fields → nothing to generate"* and skip it; do **not** write an empty field-map.
|
|
74
|
+
Those scenarios are covered by the data create-test already produced, not by field-level test data.
|
|
75
|
+
(This is a correct outcome, not a gap — don't force a field-map to make the unit "processed".)
|
|
76
|
+
|
|
77
|
+
## Blind spots — ask, never fabricate
|
|
78
|
+
|
|
79
|
+
If a field's type is **not** in the catalog — a business enum, a cross-entity constraint, a
|
|
80
|
+
project-specific data model — do **not** guess. Ask the QA (via `AskUserQuestion`) for: (1) the valid
|
|
81
|
+
set, (2) invalid / non-existent values, (3) the error code when wrong, (4) dependencies on other
|
|
82
|
+
fields. Record it in `qa/data-factory/project/<entity>.yaml` so it becomes reusable project catalog.
|
|
83
|
+
This is the core discipline: *common is never enough; when blind, think and ask — don't invent.*
|
|
84
|
+
|
|
85
|
+
## Customizing the catalog — `.overwrite` (survives `sungen update`)
|
|
86
|
+
|
|
87
|
+
The shipped `common/` is a floor. Project rules go in `qa/data-factory/common.overwrite.yaml`,
|
|
88
|
+
deep-merged over the common (**later-wins**, keyed by `type` + data-point `id`): same `id` replaces,
|
|
89
|
+
new `id` appends, `constraints` merge key-by-key. `sungen update` refreshes `common/` but never
|
|
90
|
+
touches your overlay. If an update makes an overlay entry stale (references a bank/constraint that no
|
|
91
|
+
longer exists), the loader **warns you to prune it** — it never edits your file. Prefer a targeted
|
|
92
|
+
`errorMap`/`constraints` in the field-map for per-screen tweaks; use `.overwrite` only for rules that
|
|
93
|
+
apply project-wide.
|
|
94
|
+
|
|
95
|
+
## Bulk-import fixtures (CSV)
|
|
96
|
+
|
|
97
|
+
For CSV/Excel import or bulk-upload testing, `sungen data fixture --screen <name> [--rows N] [--invalid]`
|
|
98
|
+
turns the field-map into a fixture FILE under `qa/fixtures/`: `<name>.csv` (N valid rows; nested groups
|
|
99
|
+
→ dotted headers; `unique` fields row-indexed) that should import cleanly, and — with `--invalid` —
|
|
100
|
+
`<name>.invalid.csv` (one seeded-bad row per field + a `_violation` column) for row-level rejection
|
|
101
|
+
tests. Reference it from an `@api` upload (`files:`) or a UI file input.
|
|
102
|
+
|
|
103
|
+
## Verify before done — the phuong-phap §6 checklist
|
|
104
|
+
|
|
105
|
+
Run `sungen data lint` and clear it: every required field has **≥1 valid**, **boundary present**
|
|
106
|
+
(exact min/max, not only over-boundary) for bounded types, **≥1 invalid per rule** (not one generic
|
|
107
|
+
"wrong" value for many rules), no `UNRESOLVED` values, error codes mapped or explicitly left open.
|
|
108
|
+
Then `sungen data gen` writes the standardized `test-data/<name>.yaml`. Do not hand-edit the
|
|
109
|
+
generated values — change the field-map or `.overwrite` and re-run (determinism: same input → same
|
|
110
|
+
data).
|
|
111
|
+
|
|
112
|
+
## Weaving into scenarios
|
|
113
|
+
|
|
114
|
+
When a `.feature` exists, express invalid/boundary sets as data-driven `Scenario Outline` + `Examples`
|
|
115
|
+
(`@cases`) referencing the values — see `sungen-gherkin-syntax`. This keeps one logic + many values
|
|
116
|
+
instead of copy-pasted scenarios, and each row carries its `CHK-*` id for traceability.
|
|
@@ -143,14 +143,24 @@ Scenario: ...
|
|
|
143
143
|
|
|
144
144
|
Path access on a bound result: `{{q.count}}`/`{{q.length}}`, `{{q.first.col}}`, `{{q.last.col}}`, `{{q[2].col}}`, `{{q.col}}` (= first row's col). `expect A is B` also supports `is at least` / `is at most` / `is not`. Tier-2 declarative (trivial inline, no catalog): `User see [<table>] row where [<col>] is {{v}} [has [<col2>] = "x"]`, `… no row where …`, `… count is {{n}}`. Full grammar + catalog/datasource/secret rules → **Advanced → Database** doc. Only emit DB steps when the project has a `database/` catalog / `datasources.yaml`.
|
|
145
145
|
|
|
146
|
+
### NoSQL engines (Cosmos, MongoDB, DynamoDB) — natively supported
|
|
147
|
+
|
|
148
|
+
Beyond the three SQL engines, the Data Driver has native NoSQL engines. Declarative steps (`User see [table] row where …`, `… no row …`, `… count is …`) work across ALL of them; only the raw `@query` catalog shape differs per engine:
|
|
149
|
+
|
|
150
|
+
- **`engine: cosmos`** (Azure Cosmos DB, NoSQL API) — SQL dialect. Author `sql:` catalog entries against the mandatory `c` alias (`SELECT * FROM c WHERE c.<field> = :p`); named params bind automatically. Config: `endpoint`+`key` (or a connection-string `url`), `database`, `container` (default when no `[table]`). Read-only by construction (the query API can't mutate).
|
|
151
|
+
- **`engine: mongodb`** — no SQL. Author a `find:` (`{collection, filter, projection?, sort?, limit?}`) or `pipeline:` (`{collection, stages: […]}`) catalog entry; `:param` placeholders bind at runtime. `$out`/`$merge`/`$where`/`$function`/`$accumulator`/`mapReduce` are refused. Config: a `mongodb://` `url`. Read-only = a server-side read role.
|
|
152
|
+
- **`engine: dynamodb`** — raw `@query` is PartiQL SELECT (`sql: SELECT … FROM "table" WHERE pk = :p`). Declarative steps route GetItem/Query only for partition-/sort-key filters via a `keySchema:` registry; a **non-key filter is refused** (use a raw PartiQL SELECT — never a full-table Scan). Config: `region` + `keySchema` (credentials from the AWS env chain). Read-only = IAM `PartiQLSelect`-only.
|
|
153
|
+
|
|
154
|
+
Full config + catalog examples → **Advanced → Database** doc.
|
|
155
|
+
|
|
146
156
|
### Unsupported DB engine — fallback (ask, don't improvise)
|
|
147
157
|
|
|
148
|
-
Direct SQL verification (`@query`, the Data Driver)
|
|
158
|
+
Direct SQL verification (`@query`, the Data Driver) covers these SQL engines: **`{postgres, sqlite, mysql}`** — plus the three native NoSQL engines above (Cosmos, MongoDB, DynamoDB). When a datasource's `engine` (or the DB the user describes) is **outside all of those** — e.g. Cassandra, Oracle, Neo4j, a proprietary store:
|
|
149
159
|
|
|
150
160
|
1. **Do not** attempt a direct connect. **Do not** improvise a verification method.
|
|
151
161
|
2. Present a fixed `AskUserQuestion` with exactly these 3 branches (always these, no others invented):
|
|
152
162
|
- **@api** — verify state via the project's internal API (route to the API driver).
|
|
153
|
-
- **@manual** — emit a `@manual` step + manual-check guidance (e.g.
|
|
163
|
+
- **@manual** — emit a `@manual` step + manual-check guidance (e.g. the vendor console / CLI — `cqlsh` for Cassandra, `cypher-shell` for Neo4j).
|
|
154
164
|
- **Proxy / read-replica SQL** — if the user can supply a SQL-compatible endpoint (including Azure Cosmos DB **for PostgreSQL** / Citus), treat it as `engine: postgres` (or `mysql`) and use the normal Data Driver path above.
|
|
155
165
|
3. **Recommend, don't decide.** Check the project for signals and put the best-fit branch **first**, tagged **"(Recommended)"** with a one-line reason:
|
|
156
166
|
- a project API driver / `qa/api/` catalog / `PEERCONNE_URL`-style internal API exists → **@api** first;
|
|
@@ -163,11 +173,11 @@ Direct SQL verification (`@query`, the Data Driver) only supports these engines:
|
|
|
163
173
|
Example:
|
|
164
174
|
```
|
|
165
175
|
AskUserQuestion:
|
|
166
|
-
question: "This datasource's engine (
|
|
176
|
+
question: "This datasource's engine (cassandra) isn't a supported Data Driver engine ({postgres, sqlite, mysql} + Cosmos/MongoDB/DynamoDB) — direct connect isn't supported. How should DB state be verified?"
|
|
167
177
|
options:
|
|
168
178
|
- "@api — verify via the project's internal API (Recommended: qa/api/ catalog found)"
|
|
169
|
-
- "@manual — emit a @manual step +
|
|
170
|
-
- "Proxy / read-replica SQL — supply a SQL-compatible endpoint
|
|
179
|
+
- "@manual — emit a @manual step + cqlsh check"
|
|
180
|
+
- "Proxy / read-replica SQL — supply a SQL-compatible endpoint → engine: postgres"
|
|
171
181
|
- "Other — describe your own verification approach"
|
|
172
182
|
```
|
|
173
183
|
|
|
@@ -6,6 +6,7 @@ user-invocable: false
|
|
|
6
6
|
|
|
7
7
|
## ⚠️ Gotchas — read before generating
|
|
8
8
|
|
|
9
|
+
- **Field-level test-data follows the Data Factory standard — don't hand-invent values.** When the unit has input fields, the field values (valid / boundary / invalid + error codes) come from the **`sungen-data-factory`** catalog via a field-map + `sungen data gen` (create-test step 5.3), not free-form guessing. Weave the standardized boundary/invalid sets into `@cases` with `CHK-*` trace. This skill still owns scenario structure, viewpoints, and non-field oracles.
|
|
9
10
|
- **Write incrementally — never emit the whole suite in one response.** Build the `.feature` in batches via successive `Write`/`Edit` (≈10–15 scenarios per call). For **Full coverage**, write tier-by-tier: `Write` Tier 1 → `Edit` append Tier 2 → `Edit` append Tier 3.
|
|
10
11
|
→ One huge `Write` can exceed the model's output-token cap → `API Error: Claude's response exceeded the N output token maximum`. Single-pass full coverage only fits when `CLAUDE_CODE_MAX_OUTPUT_TOKENS ≥ 64000`; otherwise batch. Batching also lets the audit/reviewer run per batch — higher quality.
|
|
11
12
|
|
|
@@ -1,36 +1,226 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* `build()` always emits PG-style `$1..$n` placeholders; mysql2/better-sqlite3 both bind with
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
2
|
+
* `build()` always emits PG-style `$1..$n` placeholders; mysql2/better-sqlite3 both bind with
|
|
3
|
+
* positional `?` (left-to-right). Rewriting `$n`→`?` must therefore ALSO reorder the params to the
|
|
4
|
+
* textual placeholder order: an author-written catalog query `WHERE a = $2 AND b = $1` becomes
|
|
5
|
+
* `a = ? AND b = ?`, so the params must be bound `[params[1], params[0]]` — otherwise the values
|
|
6
|
+
* silently swap and the query returns wrong rows. A repeated `$k` duplicates its value at each `?`.
|
|
7
|
+
* Postgres binds `$n` natively, so it is returned untouched.
|
|
8
|
+
*
|
|
9
|
+
* Only real placeholders are rewritten — a `$1` inside a single-quoted string literal (e.g.
|
|
10
|
+
* `WHERE label = '$1 off'`) is left untouched. SQL escapes an inner quote by doubling it (`''`),
|
|
11
|
+
* which the simple in-string toggle handles (an even number of quotes returns to the same state).
|
|
12
|
+
*
|
|
13
|
+
* DynamoDB PartiQL binds the same positional `?` as mysql2/sqlite, so it shares this path — the
|
|
14
|
+
* reorder is essential there too (an author-written PartiQL `WHERE sk = $2 AND pk = $1`).
|
|
6
15
|
*/
|
|
7
|
-
export declare function rewritePlaceholders(engine: string, sql: string):
|
|
16
|
+
export declare function rewritePlaceholders(engine: string, sql: string, params: any[]): {
|
|
17
|
+
sql: string;
|
|
18
|
+
params: any[];
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* Engine contract — the assertion surface is engine-agnostic; each engine owns its own dialect so
|
|
22
|
+
* a NoSQL store (Cosmos / MongoDB / DynamoDB) can back the same declarative steps as SQL.
|
|
23
|
+
* findRows(table, filter, limit) — rows matching an equality filter, capped at `limit`
|
|
24
|
+
* countRows(table, filter) — count of rows matching an equality filter
|
|
25
|
+
* runQuery(label, sql, params) — a raw catalog query (read-only). SQL dialects (incl. Cosmos SQL
|
|
26
|
+
* and DynamoDB PartiQL) take a SQL string; MongoDB's non-SQL
|
|
27
|
+
* find/pipeline shapes are dispatched separately via fetchQueryShape.
|
|
28
|
+
*/
|
|
29
|
+
interface Engine {
|
|
30
|
+
findRows(table: string, filter: Record<string, any>, limit: number): Promise<any[]>;
|
|
31
|
+
countRows(table: string, filter: Record<string, any>): Promise<number>;
|
|
32
|
+
runQuery(label: string, sql: string, params: any[]): Promise<any[]>;
|
|
33
|
+
runShape?(label: string, shape: ShapeQuery, params: Record<string, any>): Promise<any[]>;
|
|
34
|
+
}
|
|
35
|
+
/** A MongoDB catalog entry's runtime shape: exactly one of find/pipeline, with `:param` placeholders bound at runtime. */
|
|
36
|
+
type ShapeQuery = {
|
|
37
|
+
find?: {
|
|
38
|
+
collection: string;
|
|
39
|
+
filter?: Record<string, any>;
|
|
40
|
+
projection?: Record<string, any>;
|
|
41
|
+
sort?: Record<string, any>;
|
|
42
|
+
limit?: number;
|
|
43
|
+
};
|
|
44
|
+
pipeline?: {
|
|
45
|
+
collection: string;
|
|
46
|
+
stages: any[];
|
|
47
|
+
};
|
|
48
|
+
};
|
|
49
|
+
/** Minimal shape of an @azure/cosmos container the engine uses — kept local so the SDK stays a lazy dep. */
|
|
50
|
+
type CosmosContainer = {
|
|
51
|
+
items: {
|
|
52
|
+
query(spec: {
|
|
53
|
+
query: string;
|
|
54
|
+
parameters: Array<{
|
|
55
|
+
name: string;
|
|
56
|
+
value: any;
|
|
57
|
+
}>;
|
|
58
|
+
}, opts?: any): {
|
|
59
|
+
fetchAll(): Promise<{
|
|
60
|
+
resources: any[];
|
|
61
|
+
}>;
|
|
62
|
+
};
|
|
63
|
+
};
|
|
64
|
+
};
|
|
65
|
+
/**
|
|
66
|
+
* Azure Cosmos DB (NoSQL API) engine. The Cosmos query API is read-only by construction — `.query()`
|
|
67
|
+
* only ever reads; mutations require item-level APIs this engine never calls — so the SELECT guard is
|
|
68
|
+
* defense-in-depth. Cosmos binds NAMED params (`@p`), so it is immune to the positional-`?` reorder
|
|
69
|
+
* hazard. A step/catalog `[table]` selects the container; `SELECT * FROM c` uses the mandatory `c` alias.
|
|
70
|
+
*/
|
|
71
|
+
export declare class CosmosEngine implements Engine {
|
|
72
|
+
private getContainer;
|
|
73
|
+
private opts;
|
|
74
|
+
constructor(getContainer: (name?: string) => CosmosContainer, opts: {
|
|
75
|
+
timeoutMs: number;
|
|
76
|
+
maxRows: number;
|
|
77
|
+
});
|
|
78
|
+
private query;
|
|
79
|
+
/** Build `c.<col> = @p<i>` equality predicates over the mandatory `c` alias. Columns are identifier-checked. */
|
|
80
|
+
private whereFor;
|
|
81
|
+
findRows(table: string, filter: Record<string, any>, limit: number): Promise<any[]>;
|
|
82
|
+
countRows(table: string, filter: Record<string, any>): Promise<number>;
|
|
83
|
+
runQuery(label: string, sql: string, params: any[]): Promise<any[]>;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Rewrite compiled positional `$n` placeholders to Cosmos NAMED params (`@pN`) and collect the
|
|
87
|
+
* matching parameter list. A `$n` inside a single-quoted string literal is left untouched (same
|
|
88
|
+
* in-string toggle as the SQL rewrite); a repeated `$n` reuses its one `@pN` entry.
|
|
89
|
+
*/
|
|
90
|
+
export declare function cosmosBindParams(sql: string, params: any[]): {
|
|
91
|
+
query: string;
|
|
92
|
+
parameters: Array<{
|
|
93
|
+
name: string;
|
|
94
|
+
value: any;
|
|
95
|
+
}>;
|
|
96
|
+
};
|
|
97
|
+
/** Minimal shapes of the mongodb driver objects the engine uses — kept local so the SDK stays a lazy dep. */
|
|
98
|
+
type MongoCursor = {
|
|
99
|
+
project(p: any): MongoCursor;
|
|
100
|
+
sort(s: any): MongoCursor;
|
|
101
|
+
limit(n: number): MongoCursor;
|
|
102
|
+
maxTimeMS(t: number): MongoCursor;
|
|
103
|
+
toArray(): Promise<any[]>;
|
|
104
|
+
};
|
|
105
|
+
type MongoCollection = {
|
|
106
|
+
find(filter: any): MongoCursor;
|
|
107
|
+
countDocuments(filter: any, opts?: any): Promise<number>;
|
|
108
|
+
aggregate(pipeline: any[], opts?: any): {
|
|
109
|
+
toArray(): Promise<any[]>;
|
|
110
|
+
};
|
|
111
|
+
};
|
|
112
|
+
/**
|
|
113
|
+
* MongoDB engine. There is no SQL — declarative steps map to native find()/countDocuments(), and the
|
|
114
|
+
* raw `@query` path takes a find/pipeline shape (via fetchQueryShape → runShape) rather than a SQL
|
|
115
|
+
* string. Type-strict matching (`{age:30}` ≠ `{age:"30"}`) is why DB params bind via raw() (native
|
|
116
|
+
* type), not the stringifying get(). Read-only is enforced first by the server-side read role, with
|
|
117
|
+
* the operator blocklist as in-driver defense-in-depth. Per-query `maxTimeMS` + row cap bound cost.
|
|
118
|
+
*/
|
|
119
|
+
export declare class MongoEngine implements Engine {
|
|
120
|
+
private getCollection;
|
|
121
|
+
private opts;
|
|
122
|
+
constructor(getCollection: (name: string) => MongoCollection, opts: {
|
|
123
|
+
timeoutMs: number;
|
|
124
|
+
maxRows: number;
|
|
125
|
+
});
|
|
126
|
+
findRows(table: string, filter: Record<string, any>, limit: number): Promise<any[]>;
|
|
127
|
+
countRows(table: string, filter: Record<string, any>): Promise<number>;
|
|
128
|
+
runQuery(label: string): Promise<any[]>;
|
|
129
|
+
runShape(label: string, shape: ShapeQuery, params: Record<string, any>): Promise<any[]>;
|
|
130
|
+
}
|
|
131
|
+
/** The three read-only DynamoDB operations the engine needs, over the auto-marshalling Document client. */
|
|
132
|
+
type DynamoOps = {
|
|
133
|
+
executeStatement(input: {
|
|
134
|
+
Statement: string;
|
|
135
|
+
Parameters?: any[];
|
|
136
|
+
Limit?: number;
|
|
137
|
+
NextToken?: string;
|
|
138
|
+
}, signal?: AbortSignal): Promise<{
|
|
139
|
+
Items?: any[];
|
|
140
|
+
NextToken?: string;
|
|
141
|
+
}>;
|
|
142
|
+
getItem(input: {
|
|
143
|
+
TableName: string;
|
|
144
|
+
Key: Record<string, any>;
|
|
145
|
+
}, signal?: AbortSignal): Promise<{
|
|
146
|
+
Item?: any;
|
|
147
|
+
}>;
|
|
148
|
+
query(input: {
|
|
149
|
+
TableName: string;
|
|
150
|
+
KeyConditionExpression: string;
|
|
151
|
+
ExpressionAttributeNames: Record<string, string>;
|
|
152
|
+
ExpressionAttributeValues: Record<string, any>;
|
|
153
|
+
Select?: string;
|
|
154
|
+
Limit?: number;
|
|
155
|
+
ExclusiveStartKey?: Record<string, any>;
|
|
156
|
+
}, signal?: AbortSignal): Promise<{
|
|
157
|
+
Items?: any[];
|
|
158
|
+
Count?: number;
|
|
159
|
+
LastEvaluatedKey?: Record<string, any>;
|
|
160
|
+
}>;
|
|
161
|
+
};
|
|
162
|
+
type KeySchema = Record<string, {
|
|
163
|
+
partitionKey: string;
|
|
164
|
+
sortKey?: string | null;
|
|
165
|
+
}>;
|
|
166
|
+
/**
|
|
167
|
+
* DynamoDB engine. The clean primary path is raw `@query` PartiQL SELECT (positional `?`, reordered
|
|
168
|
+
* via the shared placeholder rewrite). Declarative assertRow/count is structurally constrained by
|
|
169
|
+
* DynamoDB's key model: it routes GetItem/Query only for pk / pk+sk filters (via the `keySchema`
|
|
170
|
+
* registry) and REFUSES a non-key filter rather than falling back to an unaffordable full-table Scan.
|
|
171
|
+
* Read-only is primarily IAM (`dynamodb:PartiQLSelect` only); the SELECT guard is defense-in-depth.
|
|
172
|
+
*/
|
|
173
|
+
export declare class DynamoEngine implements Engine {
|
|
174
|
+
private ops;
|
|
175
|
+
private keySchema;
|
|
176
|
+
private opts;
|
|
177
|
+
constructor(ops: DynamoOps, keySchema: KeySchema, opts: {
|
|
178
|
+
timeoutMs: number;
|
|
179
|
+
maxRows: number;
|
|
180
|
+
});
|
|
181
|
+
private withTimeout;
|
|
182
|
+
/** Decide how a flat equality filter maps to a key-based access pattern, or refuse it. */
|
|
183
|
+
private route;
|
|
184
|
+
findRows(table: string, filter: Record<string, any>, limit: number): Promise<any[]>;
|
|
185
|
+
countRows(table: string, filter: Record<string, any>): Promise<number>;
|
|
186
|
+
runQuery(label: string, sql: string, params: any[]): Promise<any[]>;
|
|
187
|
+
}
|
|
8
188
|
declare class DataSource {
|
|
9
189
|
private configs;
|
|
10
190
|
private engines;
|
|
11
191
|
private tunnels;
|
|
12
192
|
private mysqlPools;
|
|
193
|
+
private mongoClients;
|
|
13
194
|
private cfg;
|
|
14
195
|
private engine;
|
|
15
|
-
/**
|
|
196
|
+
/** Build the lazy driver-call closure for a SQL engine (pg/mysql/sqlite), incl. the optional SSH-tunnel fallback. */
|
|
197
|
+
private sqlExec;
|
|
198
|
+
/** Build the Cosmos engine — connect via endpoint+key or a connection-string url; resolve containers by name. */
|
|
199
|
+
private cosmosEngine;
|
|
200
|
+
/** Build the MongoDB engine — one reused client per datasource; the db comes from `database` or the URI default. */
|
|
201
|
+
private mongoEngine;
|
|
202
|
+
/** Build the DynamoDB engine — region + credentials from the AWS env chain; wraps the Document client's three read ops. */
|
|
203
|
+
private dynamoEngine;
|
|
204
|
+
/** Close open MySQL pools + Mongo clients + SSH tunnels (optional explicit teardown; tunnels are unref'd so the process exits regardless). */
|
|
16
205
|
close(): void;
|
|
17
|
-
private build;
|
|
18
206
|
/** A row matching `filter` must exist; if `expected` given, assert those columns on the first match. */
|
|
19
207
|
assertRow(table: string, filter: Record<string, any>, expected?: Record<string, any>, datasource?: string): Promise<void>;
|
|
20
208
|
/** No row matching `filter` may exist. */
|
|
21
209
|
assertNoRow(table: string, filter: Record<string, any>, datasource?: string): Promise<void>;
|
|
22
210
|
/** Exactly `count` rows must match `filter`. */
|
|
23
211
|
assertCount(table: string, filter: Record<string, any>, count: number, datasource?: string): Promise<void>;
|
|
24
|
-
/** Rewrites $1/$2 placeholders to `?` for engines that don't support PG-style positional params. */
|
|
25
|
-
private sqlFor;
|
|
26
|
-
/** Read-only guard (second layer): a named query must be a single SELECT/WITH statement. */
|
|
27
|
-
private assertSelectOnly;
|
|
28
212
|
/**
|
|
29
213
|
* Run a catalog query (read-only) and return its rows. The result is bound to a `{{name}}`
|
|
30
214
|
* variable via `testData.bind(...)`, so the scenario asserts on it with `expect …` steps and
|
|
31
215
|
* path access (`{{name.count}}`, `{{name.first.col}}`, `{{name[2].col}}`).
|
|
32
216
|
*/
|
|
33
217
|
fetchQuery(label: string, sql: string, params: any[], datasource?: string): Promise<any[]>;
|
|
218
|
+
/**
|
|
219
|
+
* Run a MongoDB find/pipeline catalog query (read-only) and return its documents. Same binding
|
|
220
|
+
* contract as fetchQuery, but the query is a structured shape (not SQL) with `:param` placeholders
|
|
221
|
+
* bound from `params` at runtime. Refused on an engine that has no non-SQL shape support.
|
|
222
|
+
*/
|
|
223
|
+
fetchQueryShape(label: string, shape: ShapeQuery, params: Record<string, any>, datasource?: string): Promise<any[]>;
|
|
34
224
|
}
|
|
35
225
|
export declare const db: DataSource;
|
|
36
226
|
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"specs-db.d.ts","sourceRoot":"","sources":["../../../src/orchestrator/templates/specs-db.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"specs-db.d.ts","sourceRoot":"","sources":["../../../src/orchestrator/templates/specs-db.ts"],"names":[],"mappings":"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"}
|