firebase-contract 0.1.0-rc.15 → 0.1.0-rc.16
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/README.md
CHANGED
|
@@ -245,8 +245,10 @@ export type ProductStatusKey = keyof typeof PRODUCT_STATUS
|
|
|
245
245
|
export type ProductStatus = (typeof PRODUCT_STATUS)[ProductStatusKey]
|
|
246
246
|
```
|
|
247
247
|
|
|
248
|
-
|
|
249
|
-
|
|
248
|
+
For a plain `'OPEN' | 'DONE' | 'DISABLED'` union instead, construct the
|
|
249
|
+
TypeScript generator with `enumStyle: 'union'` via the programmatic API
|
|
250
|
+
(`createTypeScriptGenerator({ enumStyle: 'union' })`). This is a build-time
|
|
251
|
+
generator option, not a YAML contract field.
|
|
250
252
|
|
|
251
253
|
## Models
|
|
252
254
|
|
|
@@ -281,24 +283,24 @@ models:
|
|
|
281
283
|
|
|
282
284
|
| option | meaning |
|
|
283
285
|
| ----------------------------------- | -------------------------------------------------------- |
|
|
284
|
-
| `type` |
|
|
285
|
-
| `optional` |
|
|
286
|
-
| `list` |
|
|
287
|
-
| `id` |
|
|
288
|
-
| `unique` |
|
|
289
|
-
| `relation` |
|
|
290
|
-
| `col` |
|
|
291
|
-
| `default` |
|
|
292
|
-
| `literal` |
|
|
293
|
-
| `nullable` |
|
|
294
|
-
| `description` |
|
|
295
|
-
| `jsdoc` |
|
|
286
|
+
| `type` | scalar name, or an enum/model name |
|
|
287
|
+
| `optional` | value may be absent (`field?`) |
|
|
288
|
+
| `list` | value is an array of `type` |
|
|
289
|
+
| `id` | marks the (single) primary identifier |
|
|
290
|
+
| `unique` | field-level unique → `@unique` |
|
|
291
|
+
| `relation` | model-typed field is a foreign-key relation (see below) |
|
|
292
|
+
| `col` | Data Connect column dataType (Int64 PKs default to `bigserial`) |
|
|
293
|
+
| `default` | `@default(expr: …)` on the DC column |
|
|
294
|
+
| `literal` | pin to one literal value (union discriminant tags) |
|
|
295
|
+
| `nullable` | value may be `null` (`.nullable()` / `\| null`) |
|
|
296
|
+
| `description` | doc comment carried into generated output |
|
|
297
|
+
| `jsdoc` | render the description as a JSDoc block (Firestore fields) |
|
|
296
298
|
| **constraints** → | |
|
|
297
|
-
| `min` / `max` |
|
|
298
|
-
| `minLength` / `maxLength` |
|
|
299
|
-
| `nonempty` |
|
|
300
|
-
| `pattern` |
|
|
301
|
-
| `email` / `url` |
|
|
299
|
+
| `min` / `max` | numeric bounds |
|
|
300
|
+
| `minLength` / `maxLength` | string/array length bounds |
|
|
301
|
+
| `nonempty` | non-empty string/array |
|
|
302
|
+
| `pattern` | regex the string must match |
|
|
303
|
+
| `email` / `url` | string format |
|
|
302
304
|
|
|
303
305
|
Constraints flow into the Zod schemas, the API request validation, and the
|
|
304
306
|
class-validator DTOs.
|
|
@@ -307,14 +309,14 @@ class-validator DTOs.
|
|
|
307
309
|
|
|
308
310
|
| option | meaning |
|
|
309
311
|
| ------------ | ------------------------------------------------------------------ |
|
|
310
|
-
| `key` |
|
|
311
|
-
| `table` |
|
|
312
|
-
| `gqlName` |
|
|
313
|
-
| `fsName` |
|
|
314
|
-
| `directives` |
|
|
315
|
-
| `footer` |
|
|
316
|
-
| `indexes` |
|
|
317
|
-
| `sql` |
|
|
312
|
+
| `key` | composite primary key field names (else the `id: true` field) |
|
|
313
|
+
| `table` | table name override (default: snake_case pluralization) |
|
|
314
|
+
| `gqlName` | GraphQL type name override |
|
|
315
|
+
| `fsName` | Firestore-side rename (avoid collisions with table models) |
|
|
316
|
+
| `directives` | `multi` = each type-level directive on its own line |
|
|
317
|
+
| `footer` | trailing comment block after the closing `}` in the schema file |
|
|
318
|
+
| `indexes` | composite `@index`/`@unique` (`{ fields, name?, unique?, expand? }` — `expand` renders args one per line) |
|
|
319
|
+
| `sql` | raw SQL constraints (see [SQL migrations](#sql-migrations)) |
|
|
318
320
|
|
|
319
321
|
### Embedded vs relation
|
|
320
322
|
|
|
@@ -392,14 +394,14 @@ Everything below exists to reproduce real hand-written `.gql` files byte-for-byt
|
|
|
392
394
|
|
|
393
395
|
| option | meaning |
|
|
394
396
|
| --- | --- |
|
|
395
|
-
| `gqlName` |
|
|
396
|
-
| `footer` |
|
|
397
|
-
| `raw` |
|
|
398
|
-
| `single: id \|
|
|
399
|
-
| `keyArg` / `keyVars` |
|
|
400
|
-
| `whereAnd: true` |
|
|
401
|
-
| `entityDir` |
|
|
402
|
-
| `style` |
|
|
397
|
+
| `gqlName` | rendered operation name (the yml key stays unique across services) |
|
|
398
|
+
| `footer` | trailing `# …` comment block after the operation |
|
|
399
|
+
| `raw` | emit the operation body verbatim (multi-model queries, `_or` keyset cursors — field checks are skipped) |
|
|
400
|
+
| `single: id \| key` | single-row lookup (`product(id: $id)` / `product(key: { … })`) |
|
|
401
|
+
| `keyArg` / `keyVars` | update/delete key argument form and variable renames |
|
|
402
|
+
| `whereAnd: true` | render conditions as an `_and: [ … ]` array |
|
|
403
|
+
| `entityDir` | override the `operations/<entity>/` output directory |
|
|
404
|
+
| `style` | formatting hints: `signature`, `args`, `data`, `orderBy`, `auth`, `key`, `and`, `where` (`inline`/`multi`/`compact`/`bare`) |
|
|
403
405
|
|
|
404
406
|
Inputs support `var` (variable rename), `required` (override optionality),
|
|
405
407
|
`literal` (fixed value, no variable), `flat` (write the FK column directly),
|
|
@@ -573,6 +575,10 @@ firestore:
|
|
|
573
575
|
extends: [meta] # appends `_meta_: MetaSchema` (imported from './_')
|
|
574
576
|
```
|
|
575
577
|
|
|
578
|
+
`extends` referencing a fragment that is not declared is an `UNKNOWN_FRAGMENT`
|
|
579
|
+
error, not a silent no-op — a typo like `extends: [metaa]` fails generation
|
|
580
|
+
rather than quietly dropping the shared fields.
|
|
581
|
+
|
|
576
582
|
## Discriminated unions
|
|
577
583
|
|
|
578
584
|
```yaml
|
|
@@ -28,6 +28,13 @@ export declare const apis: ValidationRule;
|
|
|
28
28
|
export declare const firestore: ValidationRule;
|
|
29
29
|
/** Union variants must be known models, each carrying the discriminant field. */
|
|
30
30
|
export declare const unions: ValidationRule;
|
|
31
|
+
/**
|
|
32
|
+
* A firestore doc's `extends` must reference a declared fragment. An unknown
|
|
33
|
+
* name would otherwise resolve to no fields and silently drop the shared
|
|
34
|
+
* columns (`extends: [metaa]` → no `_meta_`), the exact silent-coercion class
|
|
35
|
+
* the contract-is-truth rules exist to kill.
|
|
36
|
+
*/
|
|
37
|
+
export declare const fragmentRefs: ValidationRule;
|
|
31
38
|
/**
|
|
32
39
|
* Type names must be unique within every output namespace — the export surface
|
|
33
40
|
* where generated identifiers converge (a split layout still merges into one
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rules.d.ts","sourceRoot":"","sources":["../../../../src/lib/_internal/validation/rules.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAkB,MAAM,mBAAmB,CAAA;AAG9D,OAAO,
|
|
1
|
+
{"version":3,"file":"rules.d.ts","sourceRoot":"","sources":["../../../../src/lib/_internal/validation/rules.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAkB,MAAM,mBAAmB,CAAA;AAG9D,OAAO,EAA2B,EAAE,EAAgB,MAAM,aAAa,CAAA;AAEvE;;;;GAIG;AACH,MAAM,MAAM,cAAc,GAAG,CAAC,EAAE,EAAE,EAAE,KAAK,UAAU,EAAE,CAAA;AAIrD,uEAAuE;AACvE,eAAO,MAAM,eAAe,EAAE,cAU3B,CAAA;AAEH,6CAA6C;AAC7C,eAAO,MAAM,UAAU,EAAE,cAQpB,CAAA;AAEL,oCAAoC;AACpC,eAAO,MAAM,mBAAmB,EAAE,cAgB9B,CAAA;AAEJ,kFAAkF;AAClF,eAAO,MAAM,UAAU,EAAE,cAiCxB,CAAA;AAED,iFAAiF;AACjF,eAAO,MAAM,QAAQ,EAAE,cA+BnB,CAAA;AAEJ,mEAAmE;AACnE,eAAO,MAAM,mBAAmB,EAAE,cAkC9B,CAAA;AAEJ,mEAAmE;AACnE,eAAO,MAAM,cAAc,EAAE,cAU1B,CAAA;AAEH,mGAAmG;AACnG,eAAO,MAAM,UAAU,EAAE,cA+ErB,CAAA;AAEJ,uFAAuF;AACvF,eAAO,MAAM,IAAI,EAAE,cAYf,CAAA;AAEJ,wFAAwF;AACxF,eAAO,MAAM,SAAS,EAAE,cA+BpB,CAAA;AAEJ,iFAAiF;AACjF,eAAO,MAAM,MAAM,EAAE,cAoBjB,CAAA;AAEJ;;;;;GAKG;AACH,eAAO,MAAM,YAAY,EAAE,cAUxB,CAAA;AAaH;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,cAAc,EAAE,cA+E5B,CAAA;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,qBAAqB,EAAE,cAmBnC,CAAA;AAED,eAAO,MAAM,aAAa,EAAE,cAAc,EAczC,CAAA"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { error, warning } from '../diagnostics.js';
|
|
2
2
|
import { constantCase } from '../generators/support/naming.js';
|
|
3
3
|
import { isTable } from '../generators/support/split.js';
|
|
4
|
-
import { findModel } from '../ir/ir.js';
|
|
4
|
+
import { findFragment, findModel } from '../ir/ir.js';
|
|
5
5
|
const IDENTIFIER = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
6
6
|
/** Every field type must resolve to a known scalar, enum, or model. */
|
|
7
7
|
export const unresolvedTypes = ir => ir.models.flatMap(model => model.fields
|
|
@@ -269,6 +269,18 @@ export const unions = ir => ir.unions.flatMap(union => {
|
|
|
269
269
|
}
|
|
270
270
|
return diagnostics;
|
|
271
271
|
});
|
|
272
|
+
/**
|
|
273
|
+
* A firestore doc's `extends` must reference a declared fragment. An unknown
|
|
274
|
+
* name would otherwise resolve to no fields and silently drop the shared
|
|
275
|
+
* columns (`extends: [metaa]` → no `_meta_`), the exact silent-coercion class
|
|
276
|
+
* the contract-is-truth rules exist to kill.
|
|
277
|
+
*/
|
|
278
|
+
export const fragmentRefs = ir => ir.firestore.flatMap(doc => doc.extends
|
|
279
|
+
.filter(name => !findFragment(ir, name))
|
|
280
|
+
.map(name => error('UNKNOWN_FRAGMENT', `Firestore doc "${doc.name}" extends unknown fragment "${name}"`, {
|
|
281
|
+
file: doc.sourceFile,
|
|
282
|
+
path: `firestore.${doc.name}.extends`,
|
|
283
|
+
})));
|
|
272
284
|
const RESERVED_OWNER = 'the generated runtime';
|
|
273
285
|
/**
|
|
274
286
|
* Type names must be unique within every output namespace — the export surface
|
|
@@ -397,5 +409,6 @@ export const DEFAULT_RULES = [
|
|
|
397
409
|
apis,
|
|
398
410
|
firestore,
|
|
399
411
|
unions,
|
|
412
|
+
fragmentRefs,
|
|
400
413
|
nameCollisions,
|
|
401
414
|
];
|