@object-ui/providers 5.0.0 → 5.0.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 +12 -0
- package/dist/UploadProvider.d.ts +45 -0
- package/dist/UploadProvider.js +83 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
package/dist/UploadProvider.d.ts
CHANGED
|
@@ -102,6 +102,51 @@ export interface AzureBlobAdapterOptions {
|
|
|
102
102
|
* via their backend.
|
|
103
103
|
*/
|
|
104
104
|
export declare function createAzureBlobAdapter(opts: AzureBlobAdapterOptions): UploadAdapter;
|
|
105
|
+
export interface ObjectStackUploadAdapterOptions {
|
|
106
|
+
/**
|
|
107
|
+
* Server base URL (e.g. `https://api.example.com`). Defaults to the
|
|
108
|
+
* current origin so apps mounted on the same host as the ObjectStack
|
|
109
|
+
* server don't need any configuration.
|
|
110
|
+
*/
|
|
111
|
+
baseUrl?: string;
|
|
112
|
+
/**
|
|
113
|
+
* Mount path of the storage service. Matches the framework default
|
|
114
|
+
* (`/api/v1/storage`). Override when the server runs the storage routes
|
|
115
|
+
* under a custom prefix.
|
|
116
|
+
* @default '/api/v1/storage'
|
|
117
|
+
*/
|
|
118
|
+
basePath?: string;
|
|
119
|
+
/**
|
|
120
|
+
* Logical key prefix forwarded to the server as `scope`. Useful for
|
|
121
|
+
* partitioning uploads (e.g. "avatars", "logos", "attachments/case").
|
|
122
|
+
*/
|
|
123
|
+
scope?: string;
|
|
124
|
+
/** Override `fetch` (tests, ServiceWorker queues). */
|
|
125
|
+
fetchImpl?: typeof fetch;
|
|
126
|
+
/**
|
|
127
|
+
* Cookie/credentials mode for cross-origin auth. Defaults to
|
|
128
|
+
* `'include'` so session cookies from `@object-ui/auth` are sent
|
|
129
|
+
* automatically.
|
|
130
|
+
* @default 'include'
|
|
131
|
+
*/
|
|
132
|
+
credentials?: RequestCredentials;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* ObjectStack presigned-upload adapter — the canonical browser-side
|
|
136
|
+
* client for `@objectstack/service-storage`.
|
|
137
|
+
*
|
|
138
|
+
* Implements the three-step protocol exposed by the server:
|
|
139
|
+
* 1. POST `/upload/presigned` → `{ uploadUrl, fileId, downloadUrl }`
|
|
140
|
+
* 2. PUT `uploadUrl` → uploads the raw bytes (HMAC token)
|
|
141
|
+
* 3. POST `/upload/complete` → marks the file ready and registers
|
|
142
|
+
* the `sys_file` record
|
|
143
|
+
*
|
|
144
|
+
* Falls back to a canonical (non-signed) download URL so the returned
|
|
145
|
+
* `UploadResult.url` is stable across signed-URL expiry — components
|
|
146
|
+
* that need a fresh signed URL can call the server's
|
|
147
|
+
* `GET /files/:fileId/url` endpoint themselves.
|
|
148
|
+
*/
|
|
149
|
+
export declare function createObjectStackUploadAdapter(opts?: ObjectStackUploadAdapterOptions): UploadAdapter;
|
|
105
150
|
export interface UploadProviderProps {
|
|
106
151
|
adapter?: UploadAdapter;
|
|
107
152
|
children: ReactNode;
|
package/dist/UploadProvider.js
CHANGED
|
@@ -108,6 +108,89 @@ export function createAzureBlobAdapter(opts) {
|
|
|
108
108
|
},
|
|
109
109
|
};
|
|
110
110
|
}
|
|
111
|
+
/**
|
|
112
|
+
* ObjectStack presigned-upload adapter — the canonical browser-side
|
|
113
|
+
* client for `@objectstack/service-storage`.
|
|
114
|
+
*
|
|
115
|
+
* Implements the three-step protocol exposed by the server:
|
|
116
|
+
* 1. POST `/upload/presigned` → `{ uploadUrl, fileId, downloadUrl }`
|
|
117
|
+
* 2. PUT `uploadUrl` → uploads the raw bytes (HMAC token)
|
|
118
|
+
* 3. POST `/upload/complete` → marks the file ready and registers
|
|
119
|
+
* the `sys_file` record
|
|
120
|
+
*
|
|
121
|
+
* Falls back to a canonical (non-signed) download URL so the returned
|
|
122
|
+
* `UploadResult.url` is stable across signed-URL expiry — components
|
|
123
|
+
* that need a fresh signed URL can call the server's
|
|
124
|
+
* `GET /files/:fileId/url` endpoint themselves.
|
|
125
|
+
*/
|
|
126
|
+
export function createObjectStackUploadAdapter(opts = {}) {
|
|
127
|
+
const fetchFn = opts.fetchImpl ?? fetch;
|
|
128
|
+
const base = (opts.baseUrl ?? '').replace(/\/$/, '');
|
|
129
|
+
const path = opts.basePath ?? '/api/v1/storage';
|
|
130
|
+
const credentials = opts.credentials ?? 'include';
|
|
131
|
+
const apiUrl = (segment) => /^https?:/i.test(segment) ? segment : `${base}${segment}`;
|
|
132
|
+
return {
|
|
133
|
+
name: 'objectstack-presigned',
|
|
134
|
+
async upload(file, options = {}) {
|
|
135
|
+
const f = file;
|
|
136
|
+
const name = ('name' in f && f.name) || 'upload';
|
|
137
|
+
const mimeType = file.type || 'application/octet-stream';
|
|
138
|
+
// 1) Request a presigned PUT URL
|
|
139
|
+
const presignRes = await fetchFn(apiUrl(`${path}/upload/presigned`), {
|
|
140
|
+
method: 'POST',
|
|
141
|
+
credentials,
|
|
142
|
+
headers: { 'Content-Type': 'application/json' },
|
|
143
|
+
signal: options.signal,
|
|
144
|
+
body: JSON.stringify({
|
|
145
|
+
filename: name,
|
|
146
|
+
mimeType,
|
|
147
|
+
size: file.size,
|
|
148
|
+
scope: opts.scope ?? options.path,
|
|
149
|
+
}),
|
|
150
|
+
});
|
|
151
|
+
if (!presignRes.ok) {
|
|
152
|
+
const text = await presignRes.text().catch(() => '');
|
|
153
|
+
throw new Error(`Presigned upload request failed (${presignRes.status}): ${text || presignRes.statusText}`);
|
|
154
|
+
}
|
|
155
|
+
const presignBody = await presignRes.json();
|
|
156
|
+
const descriptor = presignBody?.data ?? presignBody;
|
|
157
|
+
const { uploadUrl, fileId, headers: putHeaders } = descriptor;
|
|
158
|
+
if (!uploadUrl || !fileId) {
|
|
159
|
+
throw new Error('Presigned upload response missing uploadUrl/fileId');
|
|
160
|
+
}
|
|
161
|
+
// 2) Upload the raw bytes (with progress + retry)
|
|
162
|
+
await uploadWithProgress(fetchFn, apiUrl(uploadUrl), file, {
|
|
163
|
+
method: 'PUT',
|
|
164
|
+
headers: { 'Content-Type': mimeType, ...(putHeaders ?? {}) },
|
|
165
|
+
...options,
|
|
166
|
+
});
|
|
167
|
+
// 3) Mark the upload complete so the server registers sys_file
|
|
168
|
+
const completeRes = await fetchFn(apiUrl(`${path}/upload/complete`), {
|
|
169
|
+
method: 'POST',
|
|
170
|
+
credentials,
|
|
171
|
+
headers: { 'Content-Type': 'application/json' },
|
|
172
|
+
signal: options.signal,
|
|
173
|
+
body: JSON.stringify({ fileId }),
|
|
174
|
+
});
|
|
175
|
+
if (!completeRes.ok) {
|
|
176
|
+
const text = await completeRes.text().catch(() => '');
|
|
177
|
+
throw new Error(`Upload completion failed (${completeRes.status}): ${text || completeRes.statusText}`);
|
|
178
|
+
}
|
|
179
|
+
// Use the canonical (stable) download endpoint as the field value
|
|
180
|
+
// so a saved record keeps working past signed-URL expiry. This
|
|
181
|
+
// endpoint 302-redirects to a freshly-signed short-lived URL on
|
|
182
|
+
// every request, so it can be used directly as `<img src>`.
|
|
183
|
+
const stableUrl = apiUrl(`${path}/files/${encodeURIComponent(fileId)}`);
|
|
184
|
+
return {
|
|
185
|
+
url: stableUrl,
|
|
186
|
+
name,
|
|
187
|
+
size: file.size,
|
|
188
|
+
mimeType,
|
|
189
|
+
meta: { fileId, scope: opts.scope ?? options.path },
|
|
190
|
+
};
|
|
191
|
+
},
|
|
192
|
+
};
|
|
193
|
+
}
|
|
111
194
|
/**
|
|
112
195
|
* Internal helper — performs a single PUT/POST with progress reporting,
|
|
113
196
|
* exponential-backoff retry, and abort propagation.
|
package/dist/index.d.ts
CHANGED
|
@@ -6,6 +6,6 @@
|
|
|
6
6
|
export { DataSourceProvider, useDataSource } from './DataSourceProvider';
|
|
7
7
|
export { MetadataProvider, useMetadata } from './MetadataProvider';
|
|
8
8
|
export { ThemeProvider, useTheme } from './ThemeProvider';
|
|
9
|
-
export { UploadProvider, useUpload, createObjectUrlAdapter, createS3Adapter, createAzureBlobAdapter, } from './UploadProvider';
|
|
9
|
+
export { UploadProvider, useUpload, createObjectUrlAdapter, createS3Adapter, createAzureBlobAdapter, createObjectStackUploadAdapter, } from './UploadProvider';
|
|
10
10
|
export type { DataSourceProviderProps, MetadataProviderProps, ThemeProviderProps, Theme, } from './types';
|
|
11
|
-
export type { UploadAdapter, UploadOptions, UploadResult, UploadProviderProps, S3AdapterOptions, AzureBlobAdapterOptions, } from './UploadProvider';
|
|
11
|
+
export type { UploadAdapter, UploadOptions, UploadResult, UploadProviderProps, S3AdapterOptions, AzureBlobAdapterOptions, ObjectStackUploadAdapterOptions, } from './UploadProvider';
|
package/dist/index.js
CHANGED
|
@@ -6,4 +6,4 @@
|
|
|
6
6
|
export { DataSourceProvider, useDataSource } from './DataSourceProvider';
|
|
7
7
|
export { MetadataProvider, useMetadata } from './MetadataProvider';
|
|
8
8
|
export { ThemeProvider, useTheme } from './ThemeProvider';
|
|
9
|
-
export { UploadProvider, useUpload, createObjectUrlAdapter, createS3Adapter, createAzureBlobAdapter, } from './UploadProvider';
|
|
9
|
+
export { UploadProvider, useUpload, createObjectUrlAdapter, createS3Adapter, createAzureBlobAdapter, createObjectStackUploadAdapter, } from './UploadProvider';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@object-ui/providers",
|
|
3
|
-
"version": "5.0.
|
|
3
|
+
"version": "5.0.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"description": "Reusable context providers for ObjectUI applications",
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
}
|
|
25
25
|
},
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"@object-ui/types": "5.0.
|
|
27
|
+
"@object-ui/types": "5.0.2"
|
|
28
28
|
},
|
|
29
29
|
"peerDependencies": {
|
|
30
30
|
"react": "^18.0.0 || ^19.0.0",
|