@stacksjs/api 0.70.22 → 0.70.25
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 +114 -0
- package/dist/index.js +1 -4899
- package/dist/src/fetcher.d.ts +42 -0
- package/dist/src/generate-openapi.d.ts +31 -0
- package/dist/src/index.d.ts +3 -0
- package/dist/src/resource.d.ts +144 -0
- package/package.json +14 -8
- package/dist/generate-openapi.d.ts +0 -2
- package/dist/index.d.ts +0 -2
- package/dist/ofetch.d.ts +0 -103
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { BodyData, FetcherResponse, QueryParams } from './types';
|
|
2
|
+
export declare const fetcher: Fetcher;
|
|
3
|
+
declare class FetcherResponseImpl<T> implements FetcherResponse<T> {
|
|
4
|
+
public data: T;
|
|
5
|
+
public status: number;
|
|
6
|
+
public headers: Headers;
|
|
7
|
+
public isOk: boolean;
|
|
8
|
+
constructor(data: T, status: number, headers: Headers, isOk: boolean);
|
|
9
|
+
ok(): boolean;
|
|
10
|
+
created(): boolean;
|
|
11
|
+
accepted(): boolean;
|
|
12
|
+
noContent(): boolean;
|
|
13
|
+
movedPermanently(): boolean;
|
|
14
|
+
found(): boolean;
|
|
15
|
+
badRequest(): boolean;
|
|
16
|
+
unauthorized(): boolean;
|
|
17
|
+
paymentRequired(): boolean;
|
|
18
|
+
forbidden(): boolean;
|
|
19
|
+
notFound(): boolean;
|
|
20
|
+
requestTimeout(): boolean;
|
|
21
|
+
conflict(): boolean;
|
|
22
|
+
unprocessableEntity(): boolean;
|
|
23
|
+
tooManyRequests(): boolean;
|
|
24
|
+
serverError(): boolean;
|
|
25
|
+
}
|
|
26
|
+
declare class Fetcher {
|
|
27
|
+
withQueryParams(params: QueryParams): this;
|
|
28
|
+
withHeaders(headers: Record<string, string>): this;
|
|
29
|
+
accept(contentType: string): this;
|
|
30
|
+
acceptJson(): this;
|
|
31
|
+
withToken(token: string): this;
|
|
32
|
+
withBasicAuth(username: string, password: string): this;
|
|
33
|
+
withDigestAuth(username: string, password: string): this;
|
|
34
|
+
withBody<D extends BodyData>(data: D): this;
|
|
35
|
+
asForm(): this;
|
|
36
|
+
attach(name: string, content: string | Blob | File, filename?: string, headers?: Record<string, string>): this;
|
|
37
|
+
get<T = string>(url: string): Promise<FetcherResponse<T>>;
|
|
38
|
+
post<T = string, D extends BodyData = BodyData>(url: string, body?: D): Promise<FetcherResponse<T>>;
|
|
39
|
+
put<T = string, D extends BodyData = BodyData>(url: string, body?: D): Promise<FetcherResponse<T>>;
|
|
40
|
+
patch<T = string, D extends BodyData = BodyData>(url: string, body?: D): Promise<FetcherResponse<T>>;
|
|
41
|
+
delete<T = string>(url: string): Promise<FetcherResponse<T>>;
|
|
42
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Build an OpenAPI 3.0 spec from the live route registry.
|
|
3
|
+
*
|
|
4
|
+
* The previous shape called `route.routes()` (which doesn't exist —
|
|
5
|
+
* `routes` is a getter, not a callable) and read fields like
|
|
6
|
+
* `route.url`, `route.statusCode`, `route.responseSchema` that the
|
|
7
|
+
* underlying bun-router doesn't actually expose. This rewrite uses
|
|
8
|
+
* `listRegisteredRoutes()` (the canonical introspection helper) and
|
|
9
|
+
* derives request schemas from each action's `validations` block when
|
|
10
|
+
* available.
|
|
11
|
+
*
|
|
12
|
+
* Without `Action.validations`, the spec lists the path + parameters
|
|
13
|
+
* but omits a request body schema — that's still useful for clients
|
|
14
|
+
* that just want to know what endpoints exist.
|
|
15
|
+
*/
|
|
16
|
+
export declare function generateOpenApi(): Promise<OpenApiSpec>;
|
|
17
|
+
declare interface OpenApiPathItem {
|
|
18
|
+
[method: string]: {
|
|
19
|
+
summary?: string
|
|
20
|
+
operationId?: string
|
|
21
|
+
parameters?: Array<{ name: string, in: string, required: boolean, schema: { type: string } }>
|
|
22
|
+
responses: Record<string, { description: string, content?: Record<string, unknown> }>
|
|
23
|
+
requestBody?: { content: Record<string, unknown>, required?: boolean }
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
declare interface OpenApiSpec {
|
|
27
|
+
openapi: string
|
|
28
|
+
info: { title: string, version: string }
|
|
29
|
+
paths: Record<string, OpenApiPathItem>
|
|
30
|
+
components: { schemas: Record<string, unknown> }
|
|
31
|
+
}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Anonymous Resource
|
|
3
|
+
*
|
|
4
|
+
* Create a resource inline without a class.
|
|
5
|
+
*/
|
|
6
|
+
export declare function resource<T>(data: T, transform: (resource: T) => Record<string, any>): { toResponse: () => Record<string, any>, toJson: () => string };
|
|
7
|
+
/**
|
|
8
|
+
* Anonymous Collection
|
|
9
|
+
*
|
|
10
|
+
* Create a collection inline without a class.
|
|
11
|
+
*/
|
|
12
|
+
export declare function collection<T>(data: T[], transform: (resource: T) => Record<string, any>): { toResponse: () => Record<string, any>, toJson: () => string };
|
|
13
|
+
/**
|
|
14
|
+
* Pagination metadata structure
|
|
15
|
+
*/
|
|
16
|
+
export declare interface PaginationMeta {
|
|
17
|
+
current_page: number
|
|
18
|
+
from: number | null
|
|
19
|
+
last_page: number
|
|
20
|
+
per_page: number
|
|
21
|
+
to: number | null
|
|
22
|
+
total: number
|
|
23
|
+
}
|
|
24
|
+
export declare interface PaginationLinks {
|
|
25
|
+
first: string | null
|
|
26
|
+
last: string | null
|
|
27
|
+
prev: string | null
|
|
28
|
+
next: string | null
|
|
29
|
+
}
|
|
30
|
+
export declare interface PaginatedData<T> {
|
|
31
|
+
data: T[]
|
|
32
|
+
meta: PaginationMeta
|
|
33
|
+
links: PaginationLinks
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* API Resources
|
|
37
|
+
*
|
|
38
|
+
* Laravel-like API resources for transforming models and collections
|
|
39
|
+
* into JSON responses with fine-grained control.
|
|
40
|
+
*
|
|
41
|
+
* @example
|
|
42
|
+
* class UserResource extends JsonResource<User> {
|
|
43
|
+
* toArray() {
|
|
44
|
+
* return {
|
|
45
|
+
* id: this.resource.id,
|
|
46
|
+
* name: this.resource.name,
|
|
47
|
+
* email: this.resource.email,
|
|
48
|
+
* posts: PostResource.collection(this.whenLoaded('posts')),
|
|
49
|
+
* created_at: this.resource.created_at,
|
|
50
|
+
* }
|
|
51
|
+
* }
|
|
52
|
+
* }
|
|
53
|
+
*
|
|
54
|
+
* // Usage
|
|
55
|
+
* return new UserResource(user).toResponse()
|
|
56
|
+
* return UserResource.collection(users).toResponse()
|
|
57
|
+
*/
|
|
58
|
+
declare type Request = Record<string, unknown>;
|
|
59
|
+
/**
|
|
60
|
+
* Wrapper value types for conditional inclusion
|
|
61
|
+
*/
|
|
62
|
+
export declare class MissingValue {
|
|
63
|
+
static instance: unknown;
|
|
64
|
+
isMissing(): boolean;
|
|
65
|
+
}
|
|
66
|
+
export declare class MergeValue {
|
|
67
|
+
public data: Record<string, any>;
|
|
68
|
+
constructor(data: Record<string, any>);
|
|
69
|
+
}
|
|
70
|
+
export declare class ConditionalValue {
|
|
71
|
+
public condition: boolean | (() => boolean);
|
|
72
|
+
public value: any;
|
|
73
|
+
public defaultValue?: any;
|
|
74
|
+
constructor(condition: boolean | (() => boolean), value: any, defaultValue?: any);
|
|
75
|
+
resolve(): any;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Base JSON Resource class
|
|
79
|
+
*
|
|
80
|
+
* Extend this class to create custom API resources that transform
|
|
81
|
+
* your models into JSON-serializable objects.
|
|
82
|
+
*/
|
|
83
|
+
export declare abstract class JsonResource<T = unknown> {
|
|
84
|
+
resource: T;
|
|
85
|
+
additional: Record<string, any>;
|
|
86
|
+
protected request?: Request;
|
|
87
|
+
static wrap: string | null;
|
|
88
|
+
protected static additionalMeta: Record<string, any>;
|
|
89
|
+
constructor(resource: T);
|
|
90
|
+
abstract toArray(request?: Request): Record<string, any>;
|
|
91
|
+
withRequest(request: Request): this;
|
|
92
|
+
withAdditional(data: Record<string, any>): this;
|
|
93
|
+
resolve(request?: Request): Record<string, any>;
|
|
94
|
+
toResponse(request?: Request): Record<string, any>;
|
|
95
|
+
toJson(request?: Request): string;
|
|
96
|
+
toJSON(): Record<string, unknown>;
|
|
97
|
+
static collection<T, R extends JsonResource<T>>(this: new (resource: T) => R, resources: T[]): ResourceCollection<T, R>;
|
|
98
|
+
protected when<V>(condition: boolean | (() => boolean), value: V | (() => V), defaultValue?: any): V | MissingValue;
|
|
99
|
+
protected whenNotNull<V>(value: V | null | undefined, transform?: (v: V) => any): any | MissingValue;
|
|
100
|
+
protected whenLoaded<V>(relationship: string, value?: V | (() => V), defaultValue?: any): V | any[] | MissingValue;
|
|
101
|
+
protected whenCounted(relationship: string, defaultValue?: number): number | MissingValue;
|
|
102
|
+
protected merge(data: Record<string, any>): MergeValue;
|
|
103
|
+
protected mergeWhen(condition: boolean | (() => boolean), data: Record<string, any> | (() => Record<string, any>)): MergeValue | MissingValue;
|
|
104
|
+
static withoutWrapping(): void;
|
|
105
|
+
static wrapWith(key: string): void;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Resource Collection
|
|
109
|
+
*
|
|
110
|
+
* Wraps an array of resources with collection-specific functionality.
|
|
111
|
+
*/
|
|
112
|
+
export declare class ResourceCollection<T = unknown, R extends JsonResource<T> = JsonResource<T>> {
|
|
113
|
+
resources: T[];
|
|
114
|
+
ResourceClass: new (resource: T) => R;
|
|
115
|
+
additional: Record<string, any>;
|
|
116
|
+
protected request?: Request;
|
|
117
|
+
static wrap: string | null;
|
|
118
|
+
constructor(resources: T[], ResourceClass: new (resource: T) => R);
|
|
119
|
+
withRequest(request: Request): this;
|
|
120
|
+
withAdditional(data: Record<string, any>): this;
|
|
121
|
+
resolve(request?: Request): Record<string, any>[];
|
|
122
|
+
toResponse(request?: Request): Record<string, any>;
|
|
123
|
+
toJson(request?: Request): string;
|
|
124
|
+
count(): number;
|
|
125
|
+
isEmpty(): boolean;
|
|
126
|
+
isNotEmpty(): boolean;
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Paginated Resource Collection
|
|
130
|
+
*
|
|
131
|
+
* Handles paginated data with meta and links.
|
|
132
|
+
*/
|
|
133
|
+
export declare class PaginatedResourceCollection<T = unknown, R extends JsonResource<T> = JsonResource<T>> extends ResourceCollection<T, R> {
|
|
134
|
+
meta: PaginationMeta;
|
|
135
|
+
links: PaginationLinks;
|
|
136
|
+
constructor(paginatedData: PaginatedData<T>, ResourceClass: new (resource: T) => R);
|
|
137
|
+
static fromPagination<T, R extends JsonResource<T>>(data: T[], ResourceClass: new (resource: T) => R, options: {
|
|
138
|
+
currentPage: number
|
|
139
|
+
perPage: number
|
|
140
|
+
total: number
|
|
141
|
+
baseUrl?: string
|
|
142
|
+
}): PaginatedResourceCollection<T, R>;
|
|
143
|
+
toResponse(request?: Request): Record<string, any>;
|
|
144
|
+
}
|
package/package.json
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stacksjs/api",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.70.
|
|
4
|
+
"version": "0.70.25",
|
|
5
5
|
"description": "The Stacks array utilities.",
|
|
6
6
|
"author": "Chris Breuer",
|
|
7
|
-
"contributors": [
|
|
7
|
+
"contributors": [
|
|
8
|
+
"Chris Breuer <chris@stacksjs.com>"
|
|
9
|
+
],
|
|
8
10
|
"license": "MIT",
|
|
9
11
|
"funding": "https://github.com/sponsors/chrisbbreuer",
|
|
10
12
|
"homepage": "https://github.com/stacksjs/stacks/tree/main/storage/framework/core/arrays#readme",
|
|
@@ -26,25 +28,29 @@
|
|
|
26
28
|
],
|
|
27
29
|
"exports": {
|
|
28
30
|
".": {
|
|
31
|
+
"bun": "./src/index.ts",
|
|
32
|
+
"types": "./dist/index.d.ts",
|
|
29
33
|
"import": "./dist/index.js"
|
|
30
34
|
},
|
|
31
35
|
"./*": {
|
|
36
|
+
"bun": "./src/*",
|
|
32
37
|
"import": "./dist/*"
|
|
33
38
|
}
|
|
34
39
|
},
|
|
35
40
|
"module": "dist/index.js",
|
|
36
41
|
"types": "dist/index.d.ts",
|
|
37
|
-
"files": [
|
|
42
|
+
"files": [
|
|
43
|
+
"README.md",
|
|
44
|
+
"dist"
|
|
45
|
+
],
|
|
38
46
|
"scripts": {
|
|
39
47
|
"build": "bun build.ts",
|
|
40
|
-
"generate-types": "
|
|
48
|
+
"generate-types": "open-api ./../../api/openapi.json --output ./../../api/api-types.ts",
|
|
41
49
|
"typecheck": "bun tsc --noEmit",
|
|
42
50
|
"prepublishOnly": "bun run build"
|
|
43
51
|
},
|
|
44
52
|
"devDependencies": {
|
|
45
|
-
"
|
|
46
|
-
"@stacksjs/utils": "0.70.
|
|
47
|
-
"ofetch": "^1.4.1",
|
|
48
|
-
"openapi-typescript": "^7.6.1"
|
|
53
|
+
"better-dx": "^0.2.12",
|
|
54
|
+
"@stacksjs/utils": "0.70.23"
|
|
49
55
|
}
|
|
50
56
|
}
|
package/dist/index.d.ts
DELETED
package/dist/ofetch.d.ts
DELETED
|
@@ -1,103 +0,0 @@
|
|
|
1
|
-
declare interface Params {
|
|
2
|
-
[key: string]: any
|
|
3
|
-
}
|
|
4
|
-
declare interface ApiFetch {
|
|
5
|
-
get: (url: string, params?: Params, header?: Headers) => Promise<FetchResponse>
|
|
6
|
-
post: (url: string, params?: Params, header?: Headers) => Promise<FetchResponse>
|
|
7
|
-
destroy: (url: string, params?: Params, header?: Headers) => Promise<FetchResponse>
|
|
8
|
-
patch: (url: string, params?: Params, header?: Headers) => Promise<FetchResponse>
|
|
9
|
-
put: (url: string, params?: Params, header?: Headers) => Promise<FetchResponse>
|
|
10
|
-
setToken: (authToken: string) => void
|
|
11
|
-
baseURL: '/' | string
|
|
12
|
-
loading: boolean
|
|
13
|
-
token: string
|
|
14
|
-
}
|
|
15
|
-
declare type FetchResponse = string | Blob | ArrayBuffer | ReadableStream<Uint8Array>
|
|
16
|
-
|
|
17
|
-
let loading = false
|
|
18
|
-
let token = ''
|
|
19
|
-
const baseURL = '/'
|
|
20
|
-
|
|
21
|
-
async function get(url: string, params?: Params, headers?: Headers): Promise<FetchResponse> {
|
|
22
|
-
if (headers) {
|
|
23
|
-
if (token)
|
|
24
|
-
headers.set('Authorization', `Bearer ${token}`)
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
return await ofetch(url, { method: 'GET', baseURL, params, headers })
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
async function post(url: string, params?: Params, headers?: Headers): Promise<any> {
|
|
31
|
-
if (headers) {
|
|
32
|
-
if (token)
|
|
33
|
-
headers.set('Authorization', `Bearer ${token}`)
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
loading = true
|
|
37
|
-
|
|
38
|
-
try {
|
|
39
|
-
const result: string | FetchResponse | Blob | ArrayBuffer | ReadableStream<Uint8Array> = await ofetch(url, {
|
|
40
|
-
method: 'POST',
|
|
41
|
-
baseURL,
|
|
42
|
-
params,
|
|
43
|
-
headers,
|
|
44
|
-
})
|
|
45
|
-
|
|
46
|
-
loading = false
|
|
47
|
-
return result
|
|
48
|
-
}
|
|
49
|
-
catch (err: any) {
|
|
50
|
-
loading = false
|
|
51
|
-
|
|
52
|
-
throw err
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
async function patch(url: string, params?: Params, headers?: Headers): Promise<FetchResponse> {
|
|
57
|
-
if (headers) {
|
|
58
|
-
if (token)
|
|
59
|
-
headers.set('Authorization', `Bearer ${token}`)
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
loading = true
|
|
63
|
-
|
|
64
|
-
return await ofetch(url, {
|
|
65
|
-
method: 'PATCH',
|
|
66
|
-
baseURL,
|
|
67
|
-
params,
|
|
68
|
-
headers,
|
|
69
|
-
})
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
async function put(url: string, params?: Params, headers?: Headers): Promise<FetchResponse> {
|
|
73
|
-
if (headers) {
|
|
74
|
-
if (token)
|
|
75
|
-
headers.set('Authorization', `Bearer ${token}`)
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
loading = true
|
|
79
|
-
|
|
80
|
-
return await ofetch(url, {
|
|
81
|
-
method: 'PUT',
|
|
82
|
-
baseURL,
|
|
83
|
-
params,
|
|
84
|
-
headers,
|
|
85
|
-
})
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
async function destroy(url: string, params?: Params, headers?: Headers): Promise<FetchResponse> {
|
|
89
|
-
if (headers) {
|
|
90
|
-
if (token)
|
|
91
|
-
headers.set('Authorization', `Bearer ${token}`)
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
loading = true
|
|
95
|
-
|
|
96
|
-
return await ofetch(url, {
|
|
97
|
-
method: 'DELETE',
|
|
98
|
-
baseURL,
|
|
99
|
-
params,
|
|
100
|
-
headers,
|
|
101
|
-
})
|
|
102
|
-
}
|
|
103
|
-
declare function setToken(authToken: string): void;
|