@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.
- package/LICENSE +21 -0
- package/README.md +458 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +75 -0
- package/dist/index.js.map +12 -0
- package/dist/lib.d.ts +6 -0
- package/dist/lib.d.ts.map +1 -0
- package/dist/operation-ids.d.ts +20 -0
- package/dist/operation-ids.d.ts.map +1 -0
- package/docs/README.md +15 -0
- package/docs/guide/auth.md +103 -0
- package/docs/guide/columns.md +117 -0
- package/docs/guide/errors.md +190 -0
- package/docs/guide/headers.md +96 -0
- package/docs/guide/operation-ids.md +113 -0
- package/docs/guide/pagination.md +138 -0
- package/docs/guide/resources.md +112 -0
- package/docs/roadmap.md +38 -0
- package/docs/troubleshooting.md +109 -0
- package/lib/auth.tsp +43 -0
- package/lib/columns.tsp +50 -0
- package/lib/errors.tsp +148 -0
- package/lib/headers.tsp +76 -0
- package/lib/main.tsp +11 -0
- package/lib/operation-ids.tsp +22 -0
- package/lib/pagination.tsp +62 -0
- package/lib/resources.tsp +63 -0
- package/lib/scalars.tsp +12 -0
- package/package.json +64 -0
package/lib/headers.tsp
ADDED
|
@@ -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
|
+
}
|
package/lib/scalars.tsp
ADDED
|
@@ -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
|
+
}
|