@pikku/fetch 0.12.0 → 0.12.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,18 @@
1
+ ## 0.12.2
2
+
3
+ ### Patch Changes
4
+
5
+ - 9060165: New realtime events system: `pikku realtime` generates a typed `PikkuRealtime` client that pairs with `PikkuRPC`. A `/events` channel can be scaffolded to fan out server events to subscribers over SSE. `pikku dev` wires `LocalEventHubService` automatically so realtime works out of the box locally. The React provider exposes `PikkuRealtime` alongside `PikkuRPC`.
6
+ - 9060165: Fix `@pikku/addon-graph` package exports so generated bootstrap files can be imported correctly. The Node.js HTTP server adapter is unified across dev, standalone, and container deployments. Next.js gains a worker-RPC transport. Date values in fetch responses now deserialise correctly.
7
+
1
8
  ## 0.12.0
2
9
 
10
+ ## 0.12.1
11
+
12
+ ### Patch Changes
13
+
14
+ - 3fbd05c: Encode URI path parameters with encodeURIComponent to prevent path injection via user-supplied data values.
15
+
3
16
  ### New Features
4
17
 
5
18
  - Auto-remove `Content-Type` header for bodyless requests (GET/DELETE)
@@ -45,6 +45,11 @@ export declare class CorePikkuFetch {
45
45
  * @param {string} serverUrl - The server URL to be set.
46
46
  */
47
47
  setServerUrl(serverUrl: string): void;
48
+ /**
49
+ * Returns the configured base server URL (without trailing slash), or
50
+ * undefined if it hasn't been set yet.
51
+ */
52
+ getServerUrl(): string | undefined;
48
53
  /**
49
54
  * Sets the JWT for authorization.
50
55
  *
@@ -61,6 +66,27 @@ export declare class CorePikkuFetch {
61
66
  get(uri: string, data: any, options?: RequestInit): Promise<any>;
62
67
  patch(uri: string, data: any, options?: RequestInit): Promise<any>;
63
68
  head(uri: string, data: any, options?: RequestInit): Promise<any>;
69
+ /**
70
+ * Uploads a file to a URL obtained from `getUploadURL`.
71
+ * Handles both presigned URLs (e.g. S3) and header-based auth (e.g. B2).
72
+ *
73
+ * @param {Object} uploadInfo - The result from the backend's `getUploadURL` call.
74
+ * @param {string} uploadInfo.uploadUrl - The URL to upload to.
75
+ * @param {string} uploadInfo.assetKey - The finalized asset key.
76
+ * @param {Record<string, string>} [uploadInfo.uploadHeaders] - Optional headers required by the storage backend.
77
+ * @param {Blob | File | Buffer | ReadableStream} body - The file content to upload.
78
+ * @param {string} [contentType] - The MIME type (used as fallback if not in uploadHeaders).
79
+ * @returns {Promise<{ assetKey: string; response: Response }>} - The asset key and raw response.
80
+ */
81
+ uploadFile(uploadInfo: {
82
+ uploadUrl: string;
83
+ assetKey: string;
84
+ uploadHeaders?: Record<string, string>;
85
+ uploadMethod?: 'PUT' | 'POST';
86
+ }, body: Blob | File | BufferSource | ReadableStream, contentType?: string): Promise<{
87
+ assetKey: string;
88
+ response: Response;
89
+ }>;
64
90
  /**
65
91
  * Makes an API request with the specified URI, method, and data, and optionally transforms dates in the response.
66
92
  *
@@ -57,6 +57,13 @@ class CorePikkuFetch {
57
57
  }
58
58
  this.options.serverUrl = serverUrl;
59
59
  }
60
+ /**
61
+ * Returns the configured base server URL (without trailing slash), or
62
+ * undefined if it hasn't been set yet.
63
+ */
64
+ getServerUrl() {
65
+ return this.options.serverUrl;
66
+ }
60
67
  /**
61
68
  * Sets the JWT for authorization.
62
69
  *
@@ -103,6 +110,36 @@ class CorePikkuFetch {
103
110
  return yield this.api(uri, 'HEAD', data, options);
104
111
  });
105
112
  }
113
+ /**
114
+ * Uploads a file to a URL obtained from `getUploadURL`.
115
+ * Handles both presigned URLs (e.g. S3) and header-based auth (e.g. B2).
116
+ *
117
+ * @param {Object} uploadInfo - The result from the backend's `getUploadURL` call.
118
+ * @param {string} uploadInfo.uploadUrl - The URL to upload to.
119
+ * @param {string} uploadInfo.assetKey - The finalized asset key.
120
+ * @param {Record<string, string>} [uploadInfo.uploadHeaders] - Optional headers required by the storage backend.
121
+ * @param {Blob | File | Buffer | ReadableStream} body - The file content to upload.
122
+ * @param {string} [contentType] - The MIME type (used as fallback if not in uploadHeaders).
123
+ * @returns {Promise<{ assetKey: string; response: Response }>} - The asset key and raw response.
124
+ */
125
+ uploadFile(uploadInfo, body, contentType) {
126
+ return __awaiter(this, void 0, void 0, function* () {
127
+ var _a;
128
+ const headers = Object.assign({}, uploadInfo.uploadHeaders);
129
+ if (!headers['Content-Type'] && contentType) {
130
+ headers['Content-Type'] = contentType;
131
+ }
132
+ const response = yield fetch(uploadInfo.uploadUrl, {
133
+ method: (_a = uploadInfo.uploadMethod) !== null && _a !== void 0 ? _a : 'PUT',
134
+ headers,
135
+ body: body,
136
+ });
137
+ if (response.status >= 400) {
138
+ throw response;
139
+ }
140
+ return { assetKey: uploadInfo.assetKey, response };
141
+ });
142
+ }
106
143
  /**
107
144
  * Makes an API request with the specified URI, method, and data, and optionally transforms dates in the response.
108
145
  *
@@ -168,7 +205,7 @@ class CorePikkuFetch {
168
205
  * @returns {any} - The transformed data.
169
206
  */
170
207
  transformDates(data) {
171
- if (!this.options.transformDate) {
208
+ if (this.options.transformDate === false) {
172
209
  return data;
173
210
  }
174
211
  return (0, transform_date_js_1.transformDates)(data);
@@ -6,5 +6,6 @@
6
6
  *
7
7
  * @module @pikku/fetch
8
8
  */
9
- export { CorePikkuFetch, CorePikkuFetchOptions, HTTPMethod, } from './core-pikku-fetch.js';
9
+ export { CorePikkuFetch } from './core-pikku-fetch.js';
10
+ export type { CorePikkuFetchOptions, HTTPMethod } from './core-pikku-fetch.js';
10
11
  export { corePikkuFetch } from './pikku-fetch.js';
@@ -28,7 +28,7 @@ const corePikkuFetch = (uri, data, options) => __awaiter(void 0, void 0, void 0,
28
28
  const keys = Object.keys(data);
29
29
  for (const key of keys) {
30
30
  if (uri.includes(`:${key}`)) {
31
- uri = uri.replace(`:${key}`, data[key]);
31
+ uri = uri.replace(`:${key}`, encodeURIComponent(String(data[key])));
32
32
  delete data[key];
33
33
  }
34
34
  }
@@ -45,6 +45,11 @@ export declare class CorePikkuFetch {
45
45
  * @param {string} serverUrl - The server URL to be set.
46
46
  */
47
47
  setServerUrl(serverUrl: string): void;
48
+ /**
49
+ * Returns the configured base server URL (without trailing slash), or
50
+ * undefined if it hasn't been set yet.
51
+ */
52
+ getServerUrl(): string | undefined;
48
53
  /**
49
54
  * Sets the JWT for authorization.
50
55
  *
@@ -61,6 +66,27 @@ export declare class CorePikkuFetch {
61
66
  get(uri: string, data: any, options?: RequestInit): Promise<any>;
62
67
  patch(uri: string, data: any, options?: RequestInit): Promise<any>;
63
68
  head(uri: string, data: any, options?: RequestInit): Promise<any>;
69
+ /**
70
+ * Uploads a file to a URL obtained from `getUploadURL`.
71
+ * Handles both presigned URLs (e.g. S3) and header-based auth (e.g. B2).
72
+ *
73
+ * @param {Object} uploadInfo - The result from the backend's `getUploadURL` call.
74
+ * @param {string} uploadInfo.uploadUrl - The URL to upload to.
75
+ * @param {string} uploadInfo.assetKey - The finalized asset key.
76
+ * @param {Record<string, string>} [uploadInfo.uploadHeaders] - Optional headers required by the storage backend.
77
+ * @param {Blob | File | Buffer | ReadableStream} body - The file content to upload.
78
+ * @param {string} [contentType] - The MIME type (used as fallback if not in uploadHeaders).
79
+ * @returns {Promise<{ assetKey: string; response: Response }>} - The asset key and raw response.
80
+ */
81
+ uploadFile(uploadInfo: {
82
+ uploadUrl: string;
83
+ assetKey: string;
84
+ uploadHeaders?: Record<string, string>;
85
+ uploadMethod?: 'PUT' | 'POST';
86
+ }, body: Blob | File | BufferSource | ReadableStream, contentType?: string): Promise<{
87
+ assetKey: string;
88
+ response: Response;
89
+ }>;
64
90
  /**
65
91
  * Makes an API request with the specified URI, method, and data, and optionally transforms dates in the response.
66
92
  *
@@ -46,6 +46,13 @@ export class CorePikkuFetch {
46
46
  }
47
47
  this.options.serverUrl = serverUrl;
48
48
  }
49
+ /**
50
+ * Returns the configured base server URL (without trailing slash), or
51
+ * undefined if it hasn't been set yet.
52
+ */
53
+ getServerUrl() {
54
+ return this.options.serverUrl;
55
+ }
49
56
  /**
50
57
  * Sets the JWT for authorization.
51
58
  *
@@ -84,6 +91,35 @@ export class CorePikkuFetch {
84
91
  async head(uri, data, options) {
85
92
  return await this.api(uri, 'HEAD', data, options);
86
93
  }
94
+ /**
95
+ * Uploads a file to a URL obtained from `getUploadURL`.
96
+ * Handles both presigned URLs (e.g. S3) and header-based auth (e.g. B2).
97
+ *
98
+ * @param {Object} uploadInfo - The result from the backend's `getUploadURL` call.
99
+ * @param {string} uploadInfo.uploadUrl - The URL to upload to.
100
+ * @param {string} uploadInfo.assetKey - The finalized asset key.
101
+ * @param {Record<string, string>} [uploadInfo.uploadHeaders] - Optional headers required by the storage backend.
102
+ * @param {Blob | File | Buffer | ReadableStream} body - The file content to upload.
103
+ * @param {string} [contentType] - The MIME type (used as fallback if not in uploadHeaders).
104
+ * @returns {Promise<{ assetKey: string; response: Response }>} - The asset key and raw response.
105
+ */
106
+ async uploadFile(uploadInfo, body, contentType) {
107
+ const headers = {
108
+ ...uploadInfo.uploadHeaders,
109
+ };
110
+ if (!headers['Content-Type'] && contentType) {
111
+ headers['Content-Type'] = contentType;
112
+ }
113
+ const response = await fetch(uploadInfo.uploadUrl, {
114
+ method: uploadInfo.uploadMethod ?? 'PUT',
115
+ headers,
116
+ body: body,
117
+ });
118
+ if (response.status >= 400) {
119
+ throw response;
120
+ }
121
+ return { assetKey: uploadInfo.assetKey, response };
122
+ }
87
123
  /**
88
124
  * Makes an API request with the specified URI, method, and data, and optionally transforms dates in the response.
89
125
  *
@@ -151,7 +187,7 @@ export class CorePikkuFetch {
151
187
  * @returns {any} - The transformed data.
152
188
  */
153
189
  transformDates(data) {
154
- if (!this.options.transformDate) {
190
+ if (this.options.transformDate === false) {
155
191
  return data;
156
192
  }
157
193
  return transformDates(data);
@@ -6,5 +6,6 @@
6
6
  *
7
7
  * @module @pikku/fetch
8
8
  */
9
- export { CorePikkuFetch, CorePikkuFetchOptions, HTTPMethod, } from './core-pikku-fetch.js';
9
+ export { CorePikkuFetch } from './core-pikku-fetch.js';
10
+ export type { CorePikkuFetchOptions, HTTPMethod } from './core-pikku-fetch.js';
10
11
  export { corePikkuFetch } from './pikku-fetch.js';
package/dist/esm/index.js CHANGED
@@ -6,5 +6,5 @@
6
6
  *
7
7
  * @module @pikku/fetch
8
8
  */
9
- export { CorePikkuFetch, } from './core-pikku-fetch.js';
9
+ export { CorePikkuFetch } from './core-pikku-fetch.js';
10
10
  export { corePikkuFetch } from './pikku-fetch.js';
@@ -15,7 +15,7 @@ export const corePikkuFetch = async (uri, data, options) => {
15
15
  const keys = Object.keys(data);
16
16
  for (const key of keys) {
17
17
  if (uri.includes(`:${key}`)) {
18
- uri = uri.replace(`:${key}`, data[key]);
18
+ uri = uri.replace(`:${key}`, encodeURIComponent(String(data[key])));
19
19
  delete data[key];
20
20
  }
21
21
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pikku/fetch",
3
- "version": "0.12.0",
3
+ "version": "0.12.2",
4
4
  "author": "yasser.fadl@gmail.com",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -21,7 +21,8 @@
21
21
  "release": "yarn build && npm test",
22
22
  "test": "bash run-tests.sh",
23
23
  "test:watch": "bash run-tests.sh --watch",
24
- "test:coverage": "bash run-tests.sh --coverage"
24
+ "test:coverage": "bash run-tests.sh --coverage",
25
+ "prepublishOnly": "yarn build"
25
26
  },
26
27
  "devDependencies": {
27
28
  "typescript": "^5.9"
@@ -72,6 +72,14 @@ export class CorePikkuFetch {
72
72
  this.options.serverUrl = serverUrl
73
73
  }
74
74
 
75
+ /**
76
+ * Returns the configured base server URL (without trailing slash), or
77
+ * undefined if it hasn't been set yet.
78
+ */
79
+ public getServerUrl(): string | undefined {
80
+ return this.options.serverUrl
81
+ }
82
+
75
83
  /**
76
84
  * Sets the JWT for authorization.
77
85
  *
@@ -114,6 +122,48 @@ export class CorePikkuFetch {
114
122
  return await this.api(uri, 'HEAD', data, options)
115
123
  }
116
124
 
125
+ /**
126
+ * Uploads a file to a URL obtained from `getUploadURL`.
127
+ * Handles both presigned URLs (e.g. S3) and header-based auth (e.g. B2).
128
+ *
129
+ * @param {Object} uploadInfo - The result from the backend's `getUploadURL` call.
130
+ * @param {string} uploadInfo.uploadUrl - The URL to upload to.
131
+ * @param {string} uploadInfo.assetKey - The finalized asset key.
132
+ * @param {Record<string, string>} [uploadInfo.uploadHeaders] - Optional headers required by the storage backend.
133
+ * @param {Blob | File | Buffer | ReadableStream} body - The file content to upload.
134
+ * @param {string} [contentType] - The MIME type (used as fallback if not in uploadHeaders).
135
+ * @returns {Promise<{ assetKey: string; response: Response }>} - The asset key and raw response.
136
+ */
137
+ public async uploadFile(
138
+ uploadInfo: {
139
+ uploadUrl: string
140
+ assetKey: string
141
+ uploadHeaders?: Record<string, string>
142
+ uploadMethod?: 'PUT' | 'POST'
143
+ },
144
+ body: Blob | File | BufferSource | ReadableStream,
145
+ contentType?: string
146
+ ): Promise<{ assetKey: string; response: Response }> {
147
+ const headers: Record<string, string> = {
148
+ ...uploadInfo.uploadHeaders,
149
+ }
150
+ if (!headers['Content-Type'] && contentType) {
151
+ headers['Content-Type'] = contentType
152
+ }
153
+
154
+ const response = await fetch(uploadInfo.uploadUrl, {
155
+ method: uploadInfo.uploadMethod ?? 'PUT',
156
+ headers,
157
+ body: body as any,
158
+ })
159
+
160
+ if (response.status >= 400) {
161
+ throw response
162
+ }
163
+
164
+ return { assetKey: uploadInfo.assetKey, response }
165
+ }
166
+
117
167
  /**
118
168
  * Makes an API request with the specified URI, method, and data, and optionally transforms dates in the response.
119
169
  *
@@ -192,7 +242,7 @@ export class CorePikkuFetch {
192
242
  * @returns {any} - The transformed data.
193
243
  */
194
244
  private transformDates(data: any): any {
195
- if (!this.options.transformDate) {
245
+ if (this.options.transformDate === false) {
196
246
  return data
197
247
  }
198
248
  return transformDates(data)
@@ -0,0 +1,11 @@
1
+ import assert from 'node:assert/strict'
2
+ import { describe, test } from 'node:test'
3
+
4
+ import { CorePikkuFetch, corePikkuFetch } from './index.js'
5
+
6
+ describe('@pikku/fetch', () => {
7
+ test('exports the public fetch client API', () => {
8
+ assert.equal(typeof CorePikkuFetch, 'function')
9
+ assert.equal(typeof corePikkuFetch, 'function')
10
+ })
11
+ })
package/src/index.ts CHANGED
@@ -7,9 +7,6 @@
7
7
  * @module @pikku/fetch
8
8
  */
9
9
 
10
- export {
11
- CorePikkuFetch,
12
- CorePikkuFetchOptions,
13
- HTTPMethod,
14
- } from './core-pikku-fetch.js'
10
+ export { CorePikkuFetch } from './core-pikku-fetch.js'
11
+ export type { CorePikkuFetchOptions, HTTPMethod } from './core-pikku-fetch.js'
15
12
  export { corePikkuFetch } from './pikku-fetch.js'
@@ -21,7 +21,7 @@ export const corePikkuFetch = async (
21
21
  const keys = Object.keys(data)
22
22
  for (const key of keys) {
23
23
  if (uri.includes(`:${key}`)) {
24
- uri = uri.replace(`:${key}`, data[key])
24
+ uri = uri.replace(`:${key}`, encodeURIComponent(String(data[key])))
25
25
  delete data[key]
26
26
  }
27
27
  }