@nxgt/typespec 0.1.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.
@@ -0,0 +1,76 @@
1
+ import "@typespec/http";
2
+
3
+ using Http;
4
+
5
+ namespace Nxgt;
6
+
7
+ /**
8
+ * The `Idempotency-Key` a client may send with a write, 1 to 255 characters.
9
+ * Spread it into an operation's parameters.
10
+ */
11
+ model IdempotencyKeyHeader {
12
+ /**
13
+ * Made up by the client, once per write it means, and sent again with every
14
+ * retry of that write: the write happens once, and a retry gets the first
15
+ * answer back. Without it, every request writes.
16
+ */
17
+ @header("Idempotency-Key")
18
+ @minLength(1)
19
+ @maxLength(255)
20
+ idempotencyKey?: string;
21
+ }
22
+
23
+ /** `Idempotent-Replayed`, on a reply to a request with an `Idempotency-Key`. */
24
+ model IdempotentReplayedHeader {
25
+ /**
26
+ * `true` when this answer is the one an earlier request with the same
27
+ * `Idempotency-Key` got, and nothing was written this time.
28
+ */
29
+ @header("Idempotent-Replayed")
30
+ idempotentReplayed?: "true";
31
+ }
32
+
33
+ /** The `RateLimit-*` headers of a rate-limited route's replies. */
34
+ model RateLimitHeaders {
35
+ /** How many requests a full window allows. */
36
+ @header("RateLimit-Limit")
37
+ rateLimitLimit?: integer;
38
+
39
+ /** How many more requests are allowed now, after this one. */
40
+ @header("RateLimit-Remaining")
41
+ rateLimitRemaining?: integer;
42
+
43
+ /** Seconds until the window is full again, rounded up. */
44
+ @header("RateLimit-Reset")
45
+ rateLimitReset?: integer;
46
+ }
47
+
48
+ /** `Retry-After`, in seconds. */
49
+ model RetryAfterHeader {
50
+ /** Seconds to wait before trying again, rounded up: a delay, never a date. */
51
+ @header("Retry-After")
52
+ retryAfter?: integer;
53
+ }
54
+
55
+ /**
56
+ * A request with this `Idempotency-Key` is still running. Retry it as it is,
57
+ * after `Retry-After`.
58
+ */
59
+ @error
60
+ model IdempotencyInProgress {
61
+ @statusCode _: 409;
62
+ ...RateLimitHeaders;
63
+ ...RetryAfterHeader;
64
+ @body body: ConflictBody;
65
+ }
66
+
67
+ /**
68
+ * This `Idempotency-Key` was already used for a request with another body. A
69
+ * new request takes a new key.
70
+ */
71
+ @error
72
+ model IdempotencyKeyReused {
73
+ @statusCode _: 422;
74
+ ...RateLimitHeaders;
75
+ @body body: UnprocessableEntityBody;
76
+ }
package/lib/main.tsp ADDED
@@ -0,0 +1,11 @@
1
+ // The library's JavaScript: its name, diagnostics and decorators, built from
2
+ // src/ into dist/.
3
+ import "../dist/index.js";
4
+ import "./errors.tsp";
5
+ import "./operation-ids.tsp";
6
+ import "./pagination.tsp";
7
+ import "./auth.tsp";
8
+ import "./scalars.tsp";
9
+ import "./columns.tsp";
10
+ import "./headers.tsp";
11
+ import "./resources.tsp";
@@ -0,0 +1,22 @@
1
+ import "@typespec/openapi";
2
+
3
+ using TypeSpec.Reflection;
4
+
5
+ namespace Nxgt;
6
+
7
+ /**
8
+ * Names each operation of the interface after its method and the interface,
9
+ * `<operation><Interface>`: `list` in `interface Pets` becomes `listPets`. An
10
+ * operation with its own `@operationId` keeps it. An interface extending a
11
+ * marked one, or an instance of a marked template, is named after itself. An
12
+ * id another operation has is an error, `duplicate-operation-id`.
13
+ *
14
+ * @example
15
+ * ```typespec
16
+ * @operationIds
17
+ * interface Pets {
18
+ * @get list(): Pet[];
19
+ * }
20
+ * ```
21
+ */
22
+ extern dec operationIds(target: Interface);
@@ -0,0 +1,62 @@
1
+ import "@typespec/http";
2
+
3
+ using Http;
4
+
5
+ namespace Nxgt;
6
+
7
+ /**
8
+ * The query of an offset page, as `@nxgt/drizzle` and `@nxgt/mongo` read it.
9
+ * Spread it into an operation's parameters.
10
+ */
11
+ model PageParameters {
12
+ /** 1-based. */
13
+ @query
14
+ @minValue(1)
15
+ page?: integer = 1;
16
+
17
+ /** How many items a page holds. A server lowers one above its maximum to it. */
18
+ @query
19
+ @minValue(1)
20
+ pageSize?: integer = 20;
21
+ }
22
+
23
+ /** One page of an offset pagination. */
24
+ @friendlyName("{name}Page", Item)
25
+ model Page<Item> {
26
+ items: Item[];
27
+
28
+ /** Every item that matches, across all pages. */
29
+ total: integer;
30
+
31
+ /** 1-based. */
32
+ page: integer;
33
+
34
+ pageSize: integer;
35
+
36
+ /** `ceil(total / pageSize)`: 0 when nothing matches. */
37
+ pageCount: integer;
38
+ }
39
+
40
+ /**
41
+ * The query of a cursor page, as `@nxgt/drizzle` and `@nxgt/mongo` read it.
42
+ * Spread it into an operation's parameters.
43
+ */
44
+ model CursorPageParameters {
45
+ /** The previous page's `nextCursor`; absent for the first page. */
46
+ @query
47
+ after?: string;
48
+
49
+ /** How many items a page holds. A server lowers one above its maximum to it. */
50
+ @query
51
+ @minValue(1)
52
+ limit?: integer = 20;
53
+ }
54
+
55
+ /** One page of a cursor pagination, which gives no total. */
56
+ @friendlyName("{name}CursorPage", Item)
57
+ model CursorPage<Item> {
58
+ items: Item[];
59
+
60
+ /** Pass it as `after` for the next page; `null` on the last one. */
61
+ nextCursor: string | null;
62
+ }
@@ -0,0 +1,63 @@
1
+ import "@typespec/http";
2
+
3
+ using Http;
4
+
5
+ namespace Nxgt;
6
+
7
+ /**
8
+ * The sort of a list, as `@nxgt/drizzle` takes it: one field, and a
9
+ * direction. `Field` is the union of the fields a client may sort by.
10
+ * Spread it into an operation's parameters.
11
+ */
12
+ model SortParameters<Field extends string> {
13
+ /** The field to sort by. */
14
+ @query
15
+ orderBy?: Field;
16
+
17
+ /** `asc` or `desc`. */
18
+ @query
19
+ direction?: "asc" | "desc" = "asc";
20
+ }
21
+
22
+ /**
23
+ * The five operations of a resource, at `/<route>` and `/<route>/{id}`:
24
+ * `list` pages by offset, with the `Filters` the spec writes and a sort by
25
+ * `SortField`; `read`, `create`, `update` (a merge patch) and `delete`.
26
+ * Extend it with `@route`; `@operationIds` names the operations after the
27
+ * interface that extends it: `listPosts`, `readPosts`…
28
+ *
29
+ * @template Item The resource's model.
30
+ * @template Filters A model of `@query` properties, spread into `list`.
31
+ * @template SortField The fields `list` may sort by; `"id"` by default.
32
+ * @template Id The type of `{id}`; `uuid` by default.
33
+ */
34
+ @operationIds
35
+ interface Resource<
36
+ Item extends {},
37
+ Filters extends {} = {},
38
+ SortField extends string = "id",
39
+ Id = uuid
40
+ > {
41
+ /** One page of the resources, filtered and sorted. */
42
+ @get
43
+ list(...Filters, ...PageParameters, ...SortParameters<SortField>): Page<Item>;
44
+
45
+ /** One resource. */
46
+ @get
47
+ read(@path id: Id): Item | NotFound;
48
+
49
+ /** Creates a resource. */
50
+ @post
51
+ create(@body item: Create<Item>): {
52
+ @statusCode _: 201;
53
+ @body item: Item;
54
+ } | BadRequest | Conflict;
55
+
56
+ /** Updates a resource with a merge patch. */
57
+ @patch
58
+ update(@path id: Id, @body item: MergePatchUpdate<Item>): Item | BadRequest | NotFound | Conflict;
59
+
60
+ /** Deletes a resource. */
61
+ @delete
62
+ delete(@path id: Id): NoContentResponse | NotFound;
63
+ }
@@ -0,0 +1,12 @@
1
+ namespace Nxgt;
2
+
3
+ // The id @nxgt/drizzle's id() and actors() columns hold by default.
4
+ /** A UUID, such as `0b8e5c1e-2f4a-4c3b-9d6e-7f8a9b0c1d2e`. */
5
+ @friendlyName("Uuid")
6
+ @format("uuid")
7
+ scalar uuid extends string;
8
+
9
+ /** An e-mail address. */
10
+ @friendlyName("Email")
11
+ @format("email")
12
+ scalar email extends string;
package/package.json ADDED
@@ -0,0 +1,64 @@
1
+ {
2
+ "name": "@nxgt/typespec",
3
+ "version": "0.1.0",
4
+ "license": "MIT",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "tspMain": "./lib/main.tsp",
9
+ "files": [
10
+ "dist",
11
+ "lib",
12
+ "docs",
13
+ "README.md",
14
+ "package.json",
15
+ "LICENSE"
16
+ ],
17
+ "exports": {
18
+ ".": {
19
+ "typespec": "./lib/main.tsp",
20
+ "types": "./dist/index.d.ts",
21
+ "import": "./dist/index.js",
22
+ "default": "./dist/index.js"
23
+ },
24
+ "./package.json": "./package.json"
25
+ },
26
+ "repository": {
27
+ "type": "git",
28
+ "url": "git+https://github.com/softistx/nxgt-http.git",
29
+ "directory": "packages/typespec"
30
+ },
31
+ "publishConfig": {
32
+ "registry": "https://registry.npmjs.org",
33
+ "access": "public"
34
+ },
35
+ "scripts": {
36
+ "build": "bun run ../../build.ts",
37
+ "fixtures": "bun run test/generate.ts",
38
+ "test": "bun run fixtures && bun test src",
39
+ "typecheck": "bun run fixtures && tsc --noEmit",
40
+ "fixtures:typespec": "bun run test/generate.ts --accept"
41
+ },
42
+ "nxgt": {
43
+ "entrypoints": [
44
+ "src/index.ts"
45
+ ]
46
+ },
47
+ "peerDependencies": {
48
+ "@typespec/compiler": "^1.16.0",
49
+ "@typespec/http": "^1.16.0",
50
+ "@typespec/openapi": "^1.16.0",
51
+ "typescript": "^6.0.3"
52
+ },
53
+ "devDependencies": {
54
+ "@nxgt/openapi-codegen": "^0.5.0",
55
+ "@nxgt/openapi-hono": "^0.3.0",
56
+ "@types/bun": "^1.4.0",
57
+ "@typespec/compiler": "1.16.0",
58
+ "@typespec/http": "1.16.0",
59
+ "@typespec/openapi": "1.16.0",
60
+ "@typespec/openapi3": "1.16.0",
61
+ "hono": "^4.13.4",
62
+ "zod": "^4.5.4"
63
+ }
64
+ }