@ciromaciel/contract 1.0.0 → 1.0.2

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 (3) hide show
  1. package/README.md +81 -19
  2. package/package.json +7 -7
  3. package/src/index.js +1 -1
package/README.md CHANGED
@@ -1,38 +1,100 @@
1
1
  # @ciromaciel/contract
2
2
 
3
- O contrato de API da stack RiLiGar: um envelope, um catálogo de códigos de erro,
4
- e os helpers que tornam impossível responder fora dele.
3
+ One response envelope, a closed catalogue of error codes, and helpers that make
4
+ answering outside it impossible.
5
5
 
6
- A norma escrita vive [no
7
- infrastructure](https://github.com/riligar/infrastructure/blob/prod/apps/website/docs/2026-09-09-api-contract.md).
8
- Este pacote é a norma **executável**: o que os produtos importam para responder,
9
- e o que os testes importam para conferir.
6
+ This package is the **executable** norm: what services import in order to
7
+ respond, and what tests import in order to check. The rules it enforces are
8
+ described below.
9
+
10
+ ## Installation
10
11
 
11
12
  ```bash
12
13
  bun add @ciromaciel/contract
13
14
  ```
14
15
 
16
+ `zod` is an **optional** peer, needed only by the `/schemas` entrypoint.
17
+
18
+ ## Quick start
19
+
15
20
  ```js
16
- import { ok, created, failure, collection } from '@ciromaciel/contract'
21
+ import { ok, created, collection, failure } from '@ciromaciel/contract'
17
22
  import { CODES } from '@ciromaciel/contract/codes'
18
23
 
19
- return ok({ id, nome })
20
- return failure(CODES.NOT_FOUND, 'projeto não encontrado')
24
+ return ok({ id, name })
25
+ return created({ id, name })
26
+ return failure(CODES.NOT_FOUND, 'project not found')
21
27
  ```
22
28
 
23
- ## Os schemas ficam num caminho próprio
29
+ `failure()` derives the HTTP status from the code — there is no signature that
30
+ lets you return `NOT_FOUND` with a 200.
31
+
32
+ ## API
33
+
34
+ ### `@ciromaciel/contract`
35
+
36
+ | Export | What it does |
37
+ | --- | --- |
38
+ | `ok(resource, { status, headers })` | One resource, at the root. No `data`, no `success`, no `message`. |
39
+ | `created(resource, { headers })` | A resource that has just been born — 201. |
40
+ | `collection(list, page, { status, headers })` | `{ items, page: { limit, offset, total, hasMore } }`. Without `total`, `total` and `hasMore` come back `null` — "nobody counted" is not the same as zero. |
41
+ | `page({ limit, offset, total, extra })` | Builds the `page` object for `collection`, requiring a numeric `total`. |
42
+ | `pagination(url, { defaultLimit, maxLimit })` | Reads `limit`/`offset` from a URL, with a ceiling. |
43
+ | `failure(code, message, { details, headers, status })` | An error `Response`. Status comes from the code. |
44
+ | `failureBody(code, message, details)` | `{ body, status }` for frameworks that assemble the response themselves. |
45
+ | `failureWithStatus(status, message, details, headers)` | For a global error handler that only has the HTTP number at hand. |
46
+ | `CODES` · `STATUS_BY_CODE` · `RETRIABLE` · `ALIASES` | The vocabulary and its tables. |
47
+ | `isCode` · `toCode` · `codeFromStatus` | Code guards and conversions. |
48
+
49
+ ### `@ciromaciel/contract/codes`
50
+
51
+ The closed enum: `UNAUTHORIZED`, `FORBIDDEN`, `WRONG_AUDIENCE`,
52
+ `INSUFFICIENT_SCOPE`, `NOT_FOUND`, `CONFLICT`, `NAME_TAKEN`, `EXPIRED`,
53
+ `HAS_RESOURCES`, `VALIDATION_ERROR`, `UNPROCESSABLE`, `RATE_LIMITED`,
54
+ `PAYLOAD_TOO_LARGE`, `PLAN_LIMIT`, `SUBSCRIPTION_REQUIRED`, `INTERNAL_ERROR`,
55
+ `SERVICE_UNAVAILABLE`, `UPSTREAM_ERROR`.
56
+
57
+ `code` is the part the client reads; `message` is for the human and its wording
58
+ may change at any time. Renaming a code is a breaking change.
59
+
60
+ ### `@ciromaciel/contract/schemas`
61
+
62
+ ```js
63
+ import { errorSchema, collectionSchema, resourceSchema, pageSchema, codeSchema } from '@ciromaciel/contract/schemas'
64
+ ```
65
+
66
+ Zod schemas for the same rules, from the reader's side. This path is separate on
67
+ purpose: `schemas.js` imports Zod, and a worker that only needs to respond
68
+ should not have to load a validator it never calls. While the index re-exported
69
+ the schemas, the import was eager — asking for `failure` pulled Zod in, and the
70
+ install broke with *"Cannot find package 'zod'"* for anyone who treated the peer
71
+ as optional.
72
+
73
+ ### `@ciromaciel/contract/plan`
24
74
 
25
75
  ```js
26
- import { envelope } from '@ciromaciel/contract/schemas' // requer zod
76
+ import { createPlanGate } from '@ciromaciel/contract/plan'
77
+
78
+ export const plan = createPlanGate({
79
+ product: 'auth',
80
+ resource: 'applications',
81
+ tiers: [
82
+ { slug: 'auth-applications-1', limit: 1 },
83
+ { slug: 'auth-applications-5', limit: 5 },
84
+ { slug: 'auth-applications-unlimited', limit: Infinity },
85
+ ],
86
+ })
27
87
  ```
28
88
 
29
- `schemas.js` importa Zod, que é um peer **opcional** — um worker que só precisa
30
- responder não deveria carregar um validador de 60 kB. Enquanto o índice
31
- reexportava os schemas, o import era eager: pedir `failure` puxava Zod junto, e
32
- a instalação quebrava com *"Cannot find package 'zod'"* para quem tratou o peer
33
- como opcional. Quem quer os schemas importa o caminho deles, onde Zod é um
34
- requisito legítimo e declarado.
89
+ Also exports `hasSlug`, `tierLimit`, `getPlanNotice`, `withPlanNotice`,
90
+ `gateTelemetry`, `recordGateDecision` and `resetGateTelemetry`. A product owns
91
+ the prefix, the slugs and the wording of the notice; the cache, the fail-open,
92
+ the product-prefix check and the choice between `PLAN_LIMIT` and
93
+ `SUBSCRIPTION_REQUIRED` are the same reasoning everywhere.
94
+
95
+ ## Links
35
96
 
36
- ## A regra que este pacote torna mecânica
97
+ - [Documentation](https://myinfrastructure.click)
98
+ - [Package on npm](https://www.npmjs.com/package/@ciromaciel/contract)
37
99
 
38
- Um agente que aprendeu um produto da RiLiGar já sabe usar os outros seis.
100
+ MIT © [Ciro Cesar Maciel](https://ciromaciel.click)
package/package.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "name": "@ciromaciel/contract",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
- "description": "The API contract of the RiLiGar stack: a single envelope, a catalogue of error codes, and the helpers that make it impossible to answer outside it.",
6
+ "description": "A single response envelope, a closed catalogue of error codes, and helpers that make answering outside it impossible.",
7
7
  "main": "src/index.js",
8
8
  "files": [
9
9
  "src",
@@ -32,9 +32,9 @@
32
32
  "publishConfig": {
33
33
  "access": "public"
34
34
  },
35
- "repository": {
36
- "type": "git",
37
- "url": "git+https://github.com/riligar/packages.git",
38
- "directory": "contract"
39
- }
35
+ "homepage": "https://myinfrastructure.click",
36
+ "bugs": {
37
+ "url": "https://myinfrastructure.click"
38
+ },
39
+ "author": "Ciro Cesar Maciel <maciel.ciro@icloud.com> (https://ciromaciel.click)"
40
40
  }
package/src/index.js CHANGED
@@ -2,7 +2,7 @@
2
2
  // @ciromaciel/contract — the API contract of the seven products
3
3
  // ============================================================================
4
4
  //
5
- // The written norm lives in https://github.com/riligar/infrastructure/blob/prod/apps/website/docs/2026-09-09-api-contract.md. This
5
+ // The written norm lives in https://github.com/ciro-maciel/infrastructure/blob/prod/docs/2026-09-09-api-contract.md. This
6
6
  // package is the EXECUTABLE norm: what the products import in order to respond,
7
7
  // and what the tests import in order to check.
8
8
  //