@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.
@@ -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,3 @@
1
+ export * from './fetcher';
2
+ export * from './generate-openapi';
3
+ export * from './resource';
@@ -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.22",
4
+ "version": "0.70.25",
5
5
  "description": "The Stacks array utilities.",
6
6
  "author": "Chris Breuer",
7
- "contributors": ["Chris Breuer <chris@stacksjs.org>"],
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": ["README.md", "dist"],
42
+ "files": [
43
+ "README.md",
44
+ "dist"
45
+ ],
38
46
  "scripts": {
39
47
  "build": "bun build.ts",
40
- "generate-types": "openapi-typescript ./../../api/openapi.json --output ./../../api/api-types.ts",
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
- "@stacksjs/development": "0.70.18",
46
- "@stacksjs/utils": "0.70.18",
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
  }
@@ -1,2 +0,0 @@
1
- export declare function generateOpenApi(): Promise<void>;
2
- declare const file: unknown;
package/dist/index.d.ts DELETED
@@ -1,2 +0,0 @@
1
- export * from './generate-openapi'
2
- export * from './ofetch'
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;