@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 CHANGED
@@ -1,5 +1,17 @@
1
1
  # @object-ui/providers — Changelog
2
2
 
3
+ ## 5.0.2
4
+
5
+ ### Patch Changes
6
+
7
+ - @object-ui/types@5.0.2
8
+
9
+ ## 5.0.1
10
+
11
+ ### Patch Changes
12
+
13
+ - @object-ui/types@5.0.1
14
+
3
15
  ## 5.0.0
4
16
 
5
17
  ### Patch Changes
@@ -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;
@@ -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.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.0"
27
+ "@object-ui/types": "5.0.2"
28
28
  },
29
29
  "peerDependencies": {
30
30
  "react": "^18.0.0 || ^19.0.0",