@revealui/core 0.8.0 → 0.9.0
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 +49 -5
- package/dist/collections/operations/sqlAdapter.d.ts.map +1 -1
- package/dist/collections/operations/sqlAdapter.js +7 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/license.d.ts +20 -0
- package/dist/license.d.ts.map +1 -1
- package/dist/license.js +30 -0
- package/dist/observability/health-check.d.ts.map +1 -1
- package/dist/observability/health-check.js +8 -1
- package/dist/server/index.d.ts +1 -1
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +1 -1
- package/dist/storage/index.d.ts +7 -6
- package/dist/storage/index.d.ts.map +1 -1
- package/dist/storage/index.js +17 -15
- package/dist/storage/object-storage.d.ts +41 -0
- package/dist/storage/object-storage.d.ts.map +1 -0
- package/dist/storage/{vercel-blob.js → object-storage.js} +38 -18
- package/dist/storage/vercel-blob-provider.d.ts +28 -0
- package/dist/storage/vercel-blob-provider.d.ts.map +1 -0
- package/dist/storage/vercel-blob-provider.js +88 -0
- package/package.json +3 -3
- package/dist/storage/vercel-blob.d.ts +0 -17
- package/dist/storage/vercel-blob.d.ts.map +0 -1
package/README.md
CHANGED
|
@@ -19,7 +19,7 @@ Part of the [RevealUI monorepo](https://github.com/RevealUIStudio/revealui) -
|
|
|
19
19
|
- **Plugins** - Extensible plugin system (form builder, nested docs, redirects)
|
|
20
20
|
- **Feature Gating** - Tier-based licensing (free, pro, max, enterprise) with JWT license keys
|
|
21
21
|
- **Database** - PostgreSQL adapters (NeonDB + PGlite for testing), connection pooling, SSL/TLS
|
|
22
|
-
- **Storage** - Pluggable storage interface (Vercel Blob adapter
|
|
22
|
+
- **Storage** - Pluggable storage interface (Cloudflare R2 primary; Vercel Blob legacy adapter retained during migration)
|
|
23
23
|
|
|
24
24
|
## Installation
|
|
25
25
|
|
|
@@ -113,27 +113,71 @@ if (isFeatureEnabled('ai')) {
|
|
|
113
113
|
|
|
114
114
|
| Subpath | Purpose |
|
|
115
115
|
|---------|---------|
|
|
116
|
-
| `@revealui/core` | Main entry
|
|
116
|
+
| `@revealui/core` | Main entry |
|
|
117
117
|
| `@revealui/core/server` | Server-side CRUD API |
|
|
118
118
|
| `@revealui/core/client` | Client-side React hooks and utilities |
|
|
119
|
-
| `@revealui/core/
|
|
119
|
+
| `@revealui/core/client/ui` | Client UI components |
|
|
120
|
+
| `@revealui/core/client/admin` | Client admin components |
|
|
121
|
+
| `@revealui/core/client/richtext` | Client rich text editor |
|
|
122
|
+
| `@revealui/core/config` | Configuration builder (`buildConfig`) |
|
|
120
123
|
| `@revealui/core/admin` | Admin dashboard components |
|
|
124
|
+
| `@revealui/core/admin/utils/serializeConfig` | Config serialization util |
|
|
125
|
+
| `@revealui/core/admin/utils/apiClient` | Admin API client |
|
|
126
|
+
| `@revealui/core/admin/i18n/en` | English i18n strings |
|
|
121
127
|
| `@revealui/core/richtext` | Rich text server utilities |
|
|
122
128
|
| `@revealui/core/richtext/client` | Lexical editor React components |
|
|
129
|
+
| `@revealui/core/richtext/react` | Rich text React exports |
|
|
123
130
|
| `@revealui/core/richtext/html` | HTML serialization |
|
|
124
131
|
| `@revealui/core/richtext/rsc` | React Server Component support |
|
|
132
|
+
| `@revealui/core/richtext/lexical` | Raw Lexical exports |
|
|
125
133
|
| `@revealui/core/security` | Security infrastructure |
|
|
126
134
|
| `@revealui/core/auth` | Auth utilities |
|
|
127
135
|
| `@revealui/core/database` | Database adapters |
|
|
128
|
-
| `@revealui/core/
|
|
136
|
+
| `@revealui/core/database/ssl-config` | SSL/TLS config |
|
|
137
|
+
| `@revealui/core/database/type-adapter` | Type adapter utilities |
|
|
138
|
+
| `@revealui/core/storage` | Storage adapters (R2 + legacy Vercel Blob) |
|
|
129
139
|
| `@revealui/core/plugins` | Plugin system |
|
|
130
140
|
| `@revealui/core/features` | Feature flags |
|
|
131
141
|
| `@revealui/core/license` | License validation |
|
|
142
|
+
| `@revealui/core/license-encryption` | License encryption |
|
|
143
|
+
| `@revealui/core/revforge-license` | RevForge license validation |
|
|
132
144
|
| `@revealui/core/monitoring` | Health monitoring |
|
|
145
|
+
| `@revealui/core/monitoring/process-registry` | Process registry |
|
|
146
|
+
| `@revealui/core/monitoring/query-monitor` | Query monitor |
|
|
147
|
+
| `@revealui/core/observability` | Observability root |
|
|
133
148
|
| `@revealui/core/observability/logger` | Structured logging |
|
|
149
|
+
| `@revealui/core/observability/metrics` | Metrics |
|
|
150
|
+
| `@revealui/core/observability/alerts` | Alerts |
|
|
151
|
+
| `@revealui/core/error-handling` | Error boundaries, retry, circuit breaker, fallback UI |
|
|
152
|
+
| `@revealui/core/error-handling/error-reporter` | Error reporting |
|
|
153
|
+
| `@revealui/core/caching` | Caching utilities |
|
|
154
|
+
| `@revealui/core/cache/query-cache` | Query cache |
|
|
155
|
+
| `@revealui/core/jobs` | Background jobs |
|
|
156
|
+
| `@revealui/core/translations` | i18n translations |
|
|
157
|
+
| `@revealui/core/ui` | UI component exports |
|
|
134
158
|
| `@revealui/core/types` | TypeScript type definitions |
|
|
159
|
+
| `@revealui/core/types/core` | Core types |
|
|
160
|
+
| `@revealui/core/types/schema` | Schema types |
|
|
161
|
+
| `@revealui/core/types/generated` | Generated types |
|
|
162
|
+
| `@revealui/core/types/admin` | Admin types |
|
|
163
|
+
| `@revealui/core/types/frontend` | Frontend types |
|
|
164
|
+
| `@revealui/core/generated` | Generated code |
|
|
135
165
|
| `@revealui/core/nextjs` | Next.js integration middleware |
|
|
136
|
-
| `@revealui/core/
|
|
166
|
+
| `@revealui/core/nextjs/withRevealUI` | Next.js config wrapper |
|
|
167
|
+
| `@revealui/core/api/rest` | REST API handler |
|
|
168
|
+
| `@revealui/core/api/compression` | Response compression |
|
|
169
|
+
| `@revealui/core/api/payload-optimization` | Payload optimization |
|
|
170
|
+
| `@revealui/core/api/rate-limit` | API rate limiting |
|
|
171
|
+
| `@revealui/core/api/response-cache` | Response cache |
|
|
172
|
+
| `@revealui/core/optimization/code-splitting` | Code splitting helpers |
|
|
173
|
+
| `@revealui/core/utils/logger` | Logger utility |
|
|
174
|
+
| `@revealui/core/utils/logger/client` | Client-side logger |
|
|
175
|
+
| `@revealui/core/utils/logger/server` | Server-side logger |
|
|
176
|
+
| `@revealui/core/utils/cache` | Cache utility |
|
|
177
|
+
| `@revealui/core/utils/deep-clone` | Deep clone utility |
|
|
178
|
+
| `@revealui/core/utils/errors` | Error utilities |
|
|
179
|
+
| `@revealui/core/utils/error-responses` | Error response helpers |
|
|
180
|
+
| `@revealui/core/utils/request-context` | Request context |
|
|
137
181
|
|
|
138
182
|
## Development
|
|
139
183
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sqlAdapter.d.ts","sourceRoot":"","sources":["../../../src/collections/operations/sqlAdapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAE3D,MAAM,MAAM,qBAAqB,GAAG;IAClC,KAAK,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,KAAK,OAAO,CAAC,cAAc,CAAC,CAAC;CACvE,CAAC;AA6BF,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAM/C;AAkBD,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAMvD;AAED,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAE3D;AAED,wBAAgB,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAG1D;AAED,wBAAgB,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAE1D;AAED,wBAAgB,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAE1D;AAED,wBAAgB,mBAAmB,CAAC,UAAU,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,MAAM,CAIpF;AAED,wBAAgB,kBAAkB,CAChC,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,MAAM,EACnB,aAAa,EAAE,MAAM,EACrB,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,MAAM,GAClB,MAAM,CAER;AAED,wBAAgB,mBAAmB,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,MAAM,CAKjF;AAED,wBAAgB,mBAAmB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,
|
|
1
|
+
{"version":3,"file":"sqlAdapter.d.ts","sourceRoot":"","sources":["../../../src/collections/operations/sqlAdapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAE3D,MAAM,MAAM,qBAAqB,GAAG;IAClC,KAAK,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,KAAK,OAAO,CAAC,cAAc,CAAC,CAAC;CACvE,CAAC;AA6BF,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAM/C;AAkBD,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAMvD;AAED,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAE3D;AAED,wBAAgB,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAG1D;AAED,wBAAgB,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAE1D;AAED,wBAAgB,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAE1D;AAED,wBAAgB,mBAAmB,CAAC,UAAU,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,MAAM,CAIpF;AAED,wBAAgB,kBAAkB,CAChC,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,MAAM,EACnB,aAAa,EAAE,MAAM,EACrB,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,MAAM,GAClB,MAAM,CAER;AAED,wBAAgB,mBAAmB,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,MAAM,CAKjF;AAED,wBAAgB,mBAAmB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAQ9D;AAED,wBAAgB,oBAAoB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAE/D;AAED,wBAAgB,eAAe,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,MAAM,CAI1E;AAED;;;;GAIG;AACH,wBAAgB,0BAA0B,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,MAAM,CAKrF"}
|
|
@@ -83,7 +83,13 @@ export function insertDocumentQuery(configSlug, columns) {
|
|
|
83
83
|
return `INSERT INTO ${collectionTable(configSlug)} (id, ${escapedColumns}) VALUES ($1, ${placeholders})`;
|
|
84
84
|
}
|
|
85
85
|
export function selectJsonByIdQuery(configSlug) {
|
|
86
|
-
|
|
86
|
+
// `id` MUST be in the projection. Query results pass through
|
|
87
|
+
// safeParseRevealDocuments, which drops any row lacking an `id`. A bare
|
|
88
|
+
// `SELECT _json` row has no `id`, so it gets filtered to empty — making
|
|
89
|
+
// update() wrongly throw "Document not found" for every JSON-field
|
|
90
|
+
// collection. Keep `_json` first so existing `sql.includes('SELECT _json')`
|
|
91
|
+
// matchers still recognize this query.
|
|
92
|
+
return `SELECT _json, id FROM ${collectionTable(configSlug)} WHERE id = $1 LIMIT 1`;
|
|
87
93
|
}
|
|
88
94
|
export function checkExistsByIdQuery(configSlug) {
|
|
89
95
|
return `SELECT id FROM ${collectionTable(configSlug)} WHERE id = $1 LIMIT 1`;
|
package/dist/index.d.ts
CHANGED
|
@@ -27,7 +27,7 @@ export { nestedDocsPlugin } from './plugins/nested-docs.js';
|
|
|
27
27
|
export { redirectsPlugin } from './plugins/redirects.js';
|
|
28
28
|
export { afterChangeTraverseFields, afterReadTraverseFields, beforeChangeTraverseFields, beforeValidateTraverseFields, buildWhereClause, checkDependencies, createRevealUIInstance, defaultLogger, extractWhereValues, flattenResult, getRelationshipFields, Logger, RevealUICollection, RevealUIGlobal, type RevealUILogger, type RichTextAdapter, relationshipPopulationPromise, validateRelationshipMetadata, withNullableJSONSchemaType, } from './revealui.js';
|
|
29
29
|
export { BoldFeature, FixedToolbarFeature, HeadingFeature, ItalicFeature, LinkFeature, lexicalEditor, TreeViewFeature, UnderlineFeature, } from './richtext/index.js';
|
|
30
|
-
export {
|
|
30
|
+
export { objectStorage } from './storage/object-storage.js';
|
|
31
31
|
export { LRUCache, type LRUCacheOptions } from './utils/cache.js';
|
|
32
32
|
export { deepClone } from './utils/deep-clone.js';
|
|
33
33
|
export { createApplicationErrorResponseData, createErrorResponseData, createSuccessResponseData, createValidationErrorResponseData, type ErrorResponseData, } from './utils/error-responses.js';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAIH,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAEtE,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACzD,YAAY,EACV,0BAA0B,EAC1B,aAAa,EACb,mBAAmB,EACnB,mBAAmB,EACnB,kBAAkB,GACnB,MAAM,mBAAmB,CAAC;AAG3B,OAAO,EACL,gBAAgB,EAChB,YAAY,EAEZ,cAAc,EACd,QAAQ,EACR,YAAY,EAEZ,SAAS,EACT,cAAc,EACd,YAAY,EACZ,qBAAqB,EACrB,oBAAoB,EACpB,SAAS,EACT,kBAAkB,EAClB,WAAW,EACX,iBAAiB,EACjB,oBAAoB,EACpB,YAAY,EACZ,yBAAyB,EACzB,UAAU,EACV,QAAQ,EAER,oBAAoB,EACpB,eAAe,EAEf,aAAa,EAEb,WAAW,EACX,kBAAkB,GACnB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAClD,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAE9C,YAAY,EAAE,8BAA8B,EAAE,MAAM,kCAAkC,CAAC;AACvF,OAAO,EAAE,wBAAwB,EAAE,MAAM,kCAAkC,CAAC;AAE5E,OAAO,EACL,cAAc,EACd,wBAAwB,EACxB,mBAAmB,EACnB,wBAAwB,EACxB,mBAAmB,GACpB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,KAAK,YAAY,EACjB,WAAW,EACX,kBAAkB,EAClB,eAAe,EACf,gBAAgB,GACjB,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,YAAY,EACZ,qBAAqB,EACrB,qBAAqB,EACrB,KAAK,iBAAiB,EACtB,kBAAkB,EAClB,cAAc,EACd,cAAc,EACd,iBAAiB,EACjB,gBAAgB,EAChB,WAAW,EACX,WAAW,EACX,iBAAiB,EACjB,UAAU,EACV,KAAK,kBAAkB,EACvB,KAAK,kBAAkB,EACvB,KAAK,WAAW,EAChB,KAAK,cAAc,EACnB,KAAK,WAAW,EAChB,iBAAiB,EACjB,kBAAkB,GACnB,MAAM,cAAc,CAAC;AAMtB,OAAO,EAAE,WAAW,IAAI,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAEnE,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAC9D,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAC5D,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAEzD,OAAO,EAEL,yBAAyB,EACzB,uBAAuB,EACvB,0BAA0B,EAC1B,4BAA4B,EAE5B,gBAAgB,EAEhB,iBAAiB,EAEjB,sBAAsB,EACtB,aAAa,EACb,kBAAkB,EAClB,aAAa,EAEb,qBAAqB,EAErB,MAAM,EAEN,kBAAkB,EAClB,cAAc,EACd,KAAK,cAAc,EAEnB,KAAK,eAAe,EACpB,6BAA6B,EAC7B,4BAA4B,EAC5B,0BAA0B,GAC3B,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,WAAW,EACX,mBAAmB,EACnB,cAAc,EACd,aAAa,EACb,WAAW,EACX,aAAa,EACb,eAAe,EACf,gBAAgB,GACjB,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -66,7 +66,7 @@ RevealUICollection, RevealUIGlobal, relationshipPopulationPromise, validateRelat
|
|
|
66
66
|
// Rich text editor
|
|
67
67
|
export { BoldFeature, FixedToolbarFeature, HeadingFeature, ItalicFeature, LinkFeature, lexicalEditor, TreeViewFeature, UnderlineFeature, } from './richtext/index.js';
|
|
68
68
|
// Storage adapters
|
|
69
|
-
export {
|
|
69
|
+
export { objectStorage } from './storage/object-storage.js';
|
|
70
70
|
// Note: Logger class is exported from ./revealui.ts (instance/logger.js), not from utils/logger.js
|
|
71
71
|
export { LRUCache } from './utils/cache.js';
|
|
72
72
|
// Deep clone utility
|
package/dist/license.d.ts
CHANGED
|
@@ -139,6 +139,26 @@ export declare function getCurrentTier(): LicenseTier;
|
|
|
139
139
|
* Returns the full license payload, or null if no valid license or cache expired.
|
|
140
140
|
*/
|
|
141
141
|
export declare function getLicensePayload(): LicensePayload | null;
|
|
142
|
+
/**
|
|
143
|
+
* Returns true when `host` is covered by the license's `domains` claim.
|
|
144
|
+
*
|
|
145
|
+
* The single matching primitive for RevForge/Fleet domain-lock — consumed by
|
|
146
|
+
* the API's `requireDomain` middleware, the admin boot check, and
|
|
147
|
+
* `validateLicenseAtStartup`. Because the allowed domains come from the signed
|
|
148
|
+
* JWT `domains` claim (not a separate env var), the lock is cryptographically
|
|
149
|
+
* bound: it cannot be spoofed by editing an env file.
|
|
150
|
+
*
|
|
151
|
+
* Matching rules (no authored regex, per the fleet no-regex rule):
|
|
152
|
+
* - `host` is lower-cased and stripped of any `:port` suffix
|
|
153
|
+
* - `localhost` / `127.0.0.1` are always allowed, so a trial kit boots and
|
|
154
|
+
* serves on its default `http://localhost` regardless of the licensed domain
|
|
155
|
+
* - otherwise `host` must equal a licensed domain OR be a subdomain of one
|
|
156
|
+
* (`app.example.com` matches `example.com`)
|
|
157
|
+
*
|
|
158
|
+
* @param host raw Host header value or URL hostname (a `:port` suffix is tolerated)
|
|
159
|
+
* @param domains the license payload's `domains` claim
|
|
160
|
+
*/
|
|
161
|
+
export declare function hostMatchesLicensedDomains(host: string, domains: readonly string[]): boolean;
|
|
142
162
|
/**
|
|
143
163
|
* Checks whether the current license is at least the given tier.
|
|
144
164
|
* Also validates that the license has not expired (checks JWT exp claim).
|
package/dist/license.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"license.d.ts","sourceRoot":"","sources":["../src/license.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"license.d.ts","sourceRoot":"","sources":["../src/license.ts"],"names":[],"mappings":"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"}
|
package/dist/license.js
CHANGED
|
@@ -281,6 +281,36 @@ export function getLicensePayload() {
|
|
|
281
281
|
evictStaleCache();
|
|
282
282
|
return cachedState.payload;
|
|
283
283
|
}
|
|
284
|
+
/**
|
|
285
|
+
* Returns true when `host` is covered by the license's `domains` claim.
|
|
286
|
+
*
|
|
287
|
+
* The single matching primitive for RevForge/Fleet domain-lock — consumed by
|
|
288
|
+
* the API's `requireDomain` middleware, the admin boot check, and
|
|
289
|
+
* `validateLicenseAtStartup`. Because the allowed domains come from the signed
|
|
290
|
+
* JWT `domains` claim (not a separate env var), the lock is cryptographically
|
|
291
|
+
* bound: it cannot be spoofed by editing an env file.
|
|
292
|
+
*
|
|
293
|
+
* Matching rules (no authored regex, per the fleet no-regex rule):
|
|
294
|
+
* - `host` is lower-cased and stripped of any `:port` suffix
|
|
295
|
+
* - `localhost` / `127.0.0.1` are always allowed, so a trial kit boots and
|
|
296
|
+
* serves on its default `http://localhost` regardless of the licensed domain
|
|
297
|
+
* - otherwise `host` must equal a licensed domain OR be a subdomain of one
|
|
298
|
+
* (`app.example.com` matches `example.com`)
|
|
299
|
+
*
|
|
300
|
+
* @param host raw Host header value or URL hostname (a `:port` suffix is tolerated)
|
|
301
|
+
* @param domains the license payload's `domains` claim
|
|
302
|
+
*/
|
|
303
|
+
export function hostMatchesLicensedDomains(host, domains) {
|
|
304
|
+
const normalized = (host.trim().toLowerCase().split(':')[0] ?? '').trim();
|
|
305
|
+
if (!normalized)
|
|
306
|
+
return false;
|
|
307
|
+
if (normalized === 'localhost' || normalized === '127.0.0.1')
|
|
308
|
+
return true;
|
|
309
|
+
return domains.some((domain) => {
|
|
310
|
+
const d = domain.trim().toLowerCase();
|
|
311
|
+
return d.length > 0 && (normalized === d || normalized.endsWith(`.${d}`));
|
|
312
|
+
});
|
|
313
|
+
}
|
|
284
314
|
/**
|
|
285
315
|
* Checks whether the current license is at least the given tier.
|
|
286
316
|
* Also validates that the license has not expired (checks JWT exp claim).
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"health-check.d.ts","sourceRoot":"","sources":["../../src/observability/health-check.ts"],"names":[],"mappings":"AAAA;;;;GAIG;
|
|
1
|
+
{"version":3,"file":"health-check.d.ts","sourceRoot":"","sources":["../../src/observability/health-check.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AASH,MAAM,MAAM,YAAY,GAAG,SAAS,GAAG,UAAU,GAAG,WAAW,CAAC;AAEhE,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,OAAO,CAAC,iBAAiB,CAAC,CAAC;IACxC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,YAAY,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,YAAY,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC;IAC1C,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,MAAM,CAAuC;IACrD,OAAO,CAAC,SAAS,CAAsB;IAEvC;;OAEG;IACH,QAAQ,CAAC,KAAK,EAAE,WAAW,GAAG,IAAI;IAIlC;;OAEG;IACH,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAI9B;;OAEG;IACG,WAAW,IAAI,OAAO,CAAC,YAAY,CAAC;IAoD1C;;OAEG;YACW,cAAc;IAS5B;;OAEG;IACH,SAAS,IAAI,MAAM;CAGpB;AAED;;GAEG;AACH,eAAO,MAAM,WAAW,mBAA0B,CAAC;AAEnD;;GAEG;AACH,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,GAAG,WAAW,CAmCnF;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,gBAAgB,GAAE,MAAW,GAAG,WAAW,CA8ClF;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,gBAAgB,GAAE,MAAW,GAAG,WAAW,CA4ChF;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,EAAE,cAAc,GAAE,MAAY,GAAG,WAAW,CAuC3F;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CACrC,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,OAAO,CAAC,iBAAiB,CAAC,EACzC,QAAQ,GAAE,OAAe,GACxB,WAAW,CAMb;AAED;;GAEG;AACH,wBAAgB,oBAAoB,UACjB,OAAO,CAAC,QAAQ,CAAC,CAanC;AAED;;GAEG;AACH,wBAAgB,uBAAuB,UACpB,OAAO,CAAC,QAAQ,CAAC,CA2BnC;AAED;;GAEG;AACH,wBAAgB,sBAAsB,UACzB,QAAQ,CAqBpB;AAED;;GAEG;AACH,wBAAsB,aAAa,CACjC,UAAU,GAAE,MAAc,EAC1B,cAAc,CAAC,EAAE,CAAC,MAAM,EAAE,YAAY,KAAK,IAAI,GAC9C,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,CAsBzB"}
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import { execFile } from 'node:child_process';
|
|
7
7
|
import { promisify } from 'node:util';
|
|
8
|
+
import { getHeapStatistics } from 'node:v8';
|
|
8
9
|
import { logger } from './logger.js';
|
|
9
10
|
const execFileAsync = promisify(execFile);
|
|
10
11
|
export class HealthCheckSystem {
|
|
@@ -145,7 +146,12 @@ export function createMemoryHealthCheck(thresholdPercent = 90) {
|
|
|
145
146
|
};
|
|
146
147
|
}
|
|
147
148
|
const usage = process.memoryUsage();
|
|
148
|
-
|
|
149
|
+
// heapTotal is V8's currently-committed heap, not the ceiling — it sits
|
|
150
|
+
// just above heapUsed and grows on demand toward heap_size_limit
|
|
151
|
+
// (--max-old-space-size), so heapUsed/heapTotal reads ~90%+ during normal
|
|
152
|
+
// operation. heap_size_limit is the real OOM-proximity denominator.
|
|
153
|
+
const heapLimit = getHeapStatistics().heap_size_limit;
|
|
154
|
+
const usedPercent = (usage.heapUsed / heapLimit) * 100;
|
|
149
155
|
let status = 'healthy';
|
|
150
156
|
let message = 'Memory usage normal';
|
|
151
157
|
if (usedPercent > thresholdPercent) {
|
|
@@ -162,6 +168,7 @@ export function createMemoryHealthCheck(thresholdPercent = 90) {
|
|
|
162
168
|
details: {
|
|
163
169
|
heapUsed: usage.heapUsed,
|
|
164
170
|
heapTotal: usage.heapTotal,
|
|
171
|
+
heapLimit,
|
|
165
172
|
usedPercent: usedPercent.toFixed(2),
|
|
166
173
|
external: usage.external,
|
|
167
174
|
rss: usage.rss,
|
package/dist/server/index.d.ts
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
*/
|
|
14
14
|
export { createRESTHandlers, handleRESTRequest } from '../api/rest.js';
|
|
15
15
|
export { universalPostgresAdapter } from '../database/universal-postgres.js';
|
|
16
|
-
export {
|
|
16
|
+
export { objectStorage } from '../storage/object-storage.js';
|
|
17
17
|
export type { LogContext, Logger, LogLevel } from '../utils/logger-server.js';
|
|
18
18
|
export { createLogger, logger } from '../utils/logger-server.js';
|
|
19
19
|
export * from '../utils/request-context.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAGH,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAEvE,OAAO,EAAE,wBAAwB,EAAE,MAAM,mCAAmC,CAAC;AAC7E,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAGH,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAEvE,OAAO,EAAE,wBAAwB,EAAE,MAAM,mCAAmC,CAAC;AAC7E,OAAO,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAC7D,YAAY,EAAE,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,2BAA2B,CAAC;AAE9E,OAAO,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,2BAA2B,CAAC;AAEjE,cAAc,6BAA6B,CAAC;AAQ5C,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC"}
|
package/dist/server/index.js
CHANGED
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
export { createRESTHandlers, handleRESTRequest } from '../api/rest.js';
|
|
16
16
|
// Server-only database/storage
|
|
17
17
|
export { universalPostgresAdapter } from '../database/universal-postgres.js';
|
|
18
|
-
export {
|
|
18
|
+
export { objectStorage } from '../storage/object-storage.js';
|
|
19
19
|
// Server-only logger with request context
|
|
20
20
|
export { createLogger, logger } from '../utils/logger-server.js';
|
|
21
21
|
// Request context utilities (server-only - uses async_hooks)
|
package/dist/storage/index.d.ts
CHANGED
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
import type { StorageConfig, StorageProvider } from './types.js';
|
|
2
2
|
export { createMockProvider } from './mock.js';
|
|
3
|
+
export type { ObjectStorageConfig } from './object-storage.js';
|
|
4
|
+
export { objectStorage } from './object-storage.js';
|
|
3
5
|
export { createR2Provider } from './r2.js';
|
|
4
6
|
export type { ListItem, ListOptions, ListResult, PutOptions, PutResult, R2Config, StorageConfig, StorageProvider, VercelBlobConfig, } from './types.js';
|
|
5
|
-
export {
|
|
7
|
+
export { createVercelBlobProvider } from './vercel-blob-provider.js';
|
|
6
8
|
/**
|
|
7
9
|
* Construct a StorageProvider from a tagged config.
|
|
8
10
|
*
|
|
9
11
|
* Per `feedback_pluggable_provider_pattern`: explicit "no-X" opt-in — unknown
|
|
10
|
-
*
|
|
12
|
+
* tags throw with a clear message; no silent fallback.
|
|
11
13
|
*
|
|
12
|
-
* Supported tags
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
* Payload plugin via `revealui.config.ts`.
|
|
14
|
+
* Supported tags: 'r2' (canonical), 'vercel-blob' (legacy migration-window
|
|
15
|
+
* fallback), and 'mock' (tests). The provider-agnostic `objectStorage` Payload
|
|
16
|
+
* plugin (object-storage.ts) adapts any of these to apps/admin's upload path.
|
|
16
17
|
*/
|
|
17
18
|
export declare function createStorage(config: StorageConfig): StorageProvider;
|
|
18
19
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/storage/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/storage/index.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAGjE,OAAO,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAC;AAC/C,YAAY,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAK/D,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAIpD,OAAO,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAC3C,YAAY,EACV,QAAQ,EACR,WAAW,EACX,UAAU,EACV,UAAU,EACV,SAAS,EACT,QAAQ,EACR,aAAa,EACb,eAAe,EACf,gBAAgB,GACjB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,wBAAwB,EAAE,MAAM,2BAA2B,CAAC;AAErE;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,aAAa,GAAG,eAAe,CAgBpE"}
|
package/dist/storage/index.js
CHANGED
|
@@ -2,43 +2,45 @@
|
|
|
2
2
|
// GAP-208 — introduced 2026-05-18 alongside Vercel Blob → Cloudflare R2 swap.
|
|
3
3
|
//
|
|
4
4
|
// Phase 1 (#959): types.ts + index.ts type re-exports.
|
|
5
|
-
// Phase 2a (
|
|
5
|
+
// Phase 2a (#959 follow-up): r2 + mock providers + createStorage factory.
|
|
6
|
+
// Phase 2b (this PR): vercel-blob StorageProvider + apps/server media-route
|
|
7
|
+
// cutover. R2 is canonical; Vercel Blob is the migration-window fallback.
|
|
6
8
|
import { createMockProvider } from './mock.js';
|
|
7
9
|
import { createR2Provider } from './r2.js';
|
|
10
|
+
import { createVercelBlobProvider } from './vercel-blob-provider.js';
|
|
8
11
|
export { createMockProvider } from './mock.js';
|
|
12
|
+
// Provider-agnostic Payload-style upload plugin — adapts any StorageProvider
|
|
13
|
+
// (R2 canonical, Vercel Blob fallback, mock) to the engine's collection upload
|
|
14
|
+
// adapter. Used by apps/admin/revealui.config.ts. GAP-208 Phase 4 replaced the
|
|
15
|
+
// provider-specific `vercelBlobStorage` plugin with this.
|
|
16
|
+
export { objectStorage } from './object-storage.js';
|
|
9
17
|
// Provider factories — exported so callers can construct a provider directly
|
|
10
18
|
// when they already know which one they want (skipping the discriminated-union
|
|
11
19
|
// factory below).
|
|
12
20
|
export { createR2Provider } from './r2.js';
|
|
13
|
-
|
|
14
|
-
// `apps/admin/revealui.config.ts` keeps working. Dropped in Phase 4 cleanup.
|
|
15
|
-
export { vercelBlobStorage } from './vercel-blob.js';
|
|
21
|
+
export { createVercelBlobProvider } from './vercel-blob-provider.js';
|
|
16
22
|
/**
|
|
17
23
|
* Construct a StorageProvider from a tagged config.
|
|
18
24
|
*
|
|
19
25
|
* Per `feedback_pluggable_provider_pattern`: explicit "no-X" opt-in — unknown
|
|
20
|
-
*
|
|
26
|
+
* tags throw with a clear message; no silent fallback.
|
|
21
27
|
*
|
|
22
|
-
* Supported tags
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
* Payload plugin via `revealui.config.ts`.
|
|
28
|
+
* Supported tags: 'r2' (canonical), 'vercel-blob' (legacy migration-window
|
|
29
|
+
* fallback), and 'mock' (tests). The provider-agnostic `objectStorage` Payload
|
|
30
|
+
* plugin (object-storage.ts) adapts any of these to apps/admin's upload path.
|
|
26
31
|
*/
|
|
27
32
|
export function createStorage(config) {
|
|
28
33
|
switch (config.provider) {
|
|
29
34
|
case 'r2':
|
|
30
35
|
return createR2Provider(config.r2);
|
|
36
|
+
case 'vercel-blob':
|
|
37
|
+
return createVercelBlobProvider(config.vercelBlob);
|
|
31
38
|
case 'mock':
|
|
32
39
|
return createMockProvider();
|
|
33
|
-
case 'vercel-blob':
|
|
34
|
-
throw new Error("createStorage: provider 'vercel-blob' is not yet implemented as a " +
|
|
35
|
-
'StorageProvider (lands in GAP-208 Phase 2b). For the migration ' +
|
|
36
|
-
'window, use the legacy `vercelBlobStorage` Payload plugin in ' +
|
|
37
|
-
"revealui.config.ts, or switch to provider: 'r2'.");
|
|
38
40
|
default: {
|
|
39
41
|
const _exhaustive = config;
|
|
40
42
|
throw new Error(`createStorage: unknown provider tag. Got ${JSON.stringify(_exhaustive)}. ` +
|
|
41
|
-
"Valid tags: 'r2', '
|
|
43
|
+
"Valid tags: 'r2', 'vercel-blob', 'mock'.");
|
|
42
44
|
}
|
|
43
45
|
}
|
|
44
46
|
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Object-storage upload plugin (provider-agnostic).
|
|
3
|
+
*
|
|
4
|
+
* Adapts any `StorageProvider` (Cloudflare R2 — canonical — Vercel Blob, mock,
|
|
5
|
+
* or a future backend) to the RevealUI engine's collection upload-adapter
|
|
6
|
+
* interface, so `apps/admin/revealui.config.ts` stores media through the
|
|
7
|
+
* configured object-storage backend. Replaces the provider-specific
|
|
8
|
+
* `vercelBlobStorage` Payload plugin retired in GAP-208 Phase 4.
|
|
9
|
+
*
|
|
10
|
+
* The backend is resolved through a lazy `resolveProvider` thunk — invoked
|
|
11
|
+
* (and memoized) on the first upload/delete, never at config-build time — so a
|
|
12
|
+
* consumer reading validated config (e.g. `@revealui/config`'s `config.storage`)
|
|
13
|
+
* does NOT force env validation when the admin config module is imported during
|
|
14
|
+
* `next build`, tests, or boot. This mirrors `apps/server` `getMediaStorage()`,
|
|
15
|
+
* which reads `config.storage` lazily per request.
|
|
16
|
+
*
|
|
17
|
+
* WARNING: This module is server-only.
|
|
18
|
+
* Do NOT import in client-side code or edge runtime.
|
|
19
|
+
*/
|
|
20
|
+
import type { Plugin } from '../types/index.js';
|
|
21
|
+
import type { StorageProvider } from './types.js';
|
|
22
|
+
export interface ObjectStorageConfig {
|
|
23
|
+
/** When `false`, the plugin is a no-op (no upload config applied). Defaults to enabled. */
|
|
24
|
+
enabled?: boolean;
|
|
25
|
+
/** Map of collection slug → whether to attach upload handling. */
|
|
26
|
+
collections?: {
|
|
27
|
+
[key: string]: boolean;
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* Lazily resolves the backend the upload adapter writes through. Called once
|
|
31
|
+
* (memoized) on the first upload/delete — never at config build — so reading
|
|
32
|
+
* validated config (e.g. `@revealui/config`'s `config.storage`) does not force
|
|
33
|
+
* env validation at module load. Should throw a clear, actionable error when
|
|
34
|
+
* no backend is configured (fail-fast; never silently no-op an upload).
|
|
35
|
+
*/
|
|
36
|
+
resolveProvider: () => StorageProvider;
|
|
37
|
+
/** Key prefix for stored objects. Defaults to `'uploads'`. */
|
|
38
|
+
prefix?: string;
|
|
39
|
+
}
|
|
40
|
+
export declare function objectStorage(config: ObjectStorageConfig): Plugin;
|
|
41
|
+
//# sourceMappingURL=object-storage.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"object-storage.d.ts","sourceRoot":"","sources":["../../src/storage/object-storage.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAGH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAElD,MAAM,WAAW,mBAAmB;IAClC,2FAA2F;IAC3F,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,kEAAkE;IAClE,WAAW,CAAC,EAAE;QACZ,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;KACxB,CAAC;IACF;;;;;;OAMG;IACH,eAAe,EAAE,MAAM,eAAe,CAAC;IACvC,8DAA8D;IAC9D,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,wBAAgB,aAAa,CAAC,MAAM,EAAE,mBAAmB,GAAG,MAAM,CAiGjE"}
|
|
@@ -1,13 +1,35 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Object-storage upload plugin (provider-agnostic).
|
|
3
|
+
*
|
|
4
|
+
* Adapts any `StorageProvider` (Cloudflare R2 — canonical — Vercel Blob, mock,
|
|
5
|
+
* or a future backend) to the RevealUI engine's collection upload-adapter
|
|
6
|
+
* interface, so `apps/admin/revealui.config.ts` stores media through the
|
|
7
|
+
* configured object-storage backend. Replaces the provider-specific
|
|
8
|
+
* `vercelBlobStorage` Payload plugin retired in GAP-208 Phase 4.
|
|
9
|
+
*
|
|
10
|
+
* The backend is resolved through a lazy `resolveProvider` thunk — invoked
|
|
11
|
+
* (and memoized) on the first upload/delete, never at config-build time — so a
|
|
12
|
+
* consumer reading validated config (e.g. `@revealui/config`'s `config.storage`)
|
|
13
|
+
* does NOT force env validation when the admin config module is imported during
|
|
14
|
+
* `next build`, tests, or boot. This mirrors `apps/server` `getMediaStorage()`,
|
|
15
|
+
* which reads `config.storage` lazily per request.
|
|
3
16
|
*
|
|
4
17
|
* WARNING: This module is server-only.
|
|
5
18
|
* Do NOT import in client-side code or edge runtime.
|
|
6
19
|
*/
|
|
7
|
-
import { del, put } from '@vercel/blob';
|
|
8
20
|
import { defaultLogger } from '../instance/logger.js';
|
|
9
|
-
export function
|
|
21
|
+
export function objectStorage(config) {
|
|
10
22
|
const prefix = config.prefix || 'uploads';
|
|
23
|
+
// Memoize the provider so the underlying client (S3 / Blob) is constructed
|
|
24
|
+
// once per process, not per upload — and so config is read lazily, on first
|
|
25
|
+
// use, rather than at config-build time.
|
|
26
|
+
let cachedProvider;
|
|
27
|
+
const getProvider = () => {
|
|
28
|
+
if (!cachedProvider) {
|
|
29
|
+
cachedProvider = config.resolveProvider();
|
|
30
|
+
}
|
|
31
|
+
return cachedProvider;
|
|
32
|
+
};
|
|
11
33
|
return (incomingConfig) => {
|
|
12
34
|
if (config.enabled === false) {
|
|
13
35
|
return incomingConfig;
|
|
@@ -26,25 +48,21 @@ export function vercelBlobStorage(config) {
|
|
|
26
48
|
crop: true,
|
|
27
49
|
adapters: [
|
|
28
50
|
{
|
|
29
|
-
name: '
|
|
51
|
+
name: 'object-storage',
|
|
30
52
|
adapter: {
|
|
31
|
-
name: '
|
|
53
|
+
name: 'object-storage',
|
|
32
54
|
generateURL: (file) => {
|
|
33
55
|
return `${prefix}/${file.filename}`;
|
|
34
56
|
},
|
|
35
57
|
upload: async (file) => {
|
|
36
58
|
try {
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
throw new Error('Vercel blob token is required but not configured');
|
|
40
|
-
}
|
|
41
|
-
const blob = await put(filePath, file.data, {
|
|
59
|
+
const key = `${prefix}/${collection.slug}/${Date.now()}-${file.name}`;
|
|
60
|
+
const result = await getProvider().put(key, file.data, {
|
|
42
61
|
access: 'public',
|
|
43
|
-
|
|
44
|
-
addRandomSuffix: false,
|
|
62
|
+
contentType: file.mimetype,
|
|
45
63
|
});
|
|
46
64
|
return {
|
|
47
|
-
url:
|
|
65
|
+
url: result.url,
|
|
48
66
|
filename: file.name,
|
|
49
67
|
filesize: file.size,
|
|
50
68
|
mimeType: file.mimetype,
|
|
@@ -53,20 +71,22 @@ export function vercelBlobStorage(config) {
|
|
|
53
71
|
};
|
|
54
72
|
}
|
|
55
73
|
catch (error) {
|
|
56
|
-
defaultLogger.error('
|
|
74
|
+
defaultLogger.error('Object storage upload error:', error);
|
|
57
75
|
throw error;
|
|
58
76
|
}
|
|
59
77
|
},
|
|
60
78
|
delete: async (filename) => {
|
|
61
79
|
try {
|
|
62
|
-
//
|
|
63
|
-
|
|
80
|
+
// Payload may pass a full URL (preferred — providers extract
|
|
81
|
+
// the storage key from it) or a bare filename; mirror the
|
|
82
|
+
// legacy plugin's prefix-prepend for the bare-filename case.
|
|
83
|
+
const keyOrUrl = filename.startsWith('http')
|
|
64
84
|
? filename
|
|
65
85
|
: `${prefix}/${filename}`;
|
|
66
|
-
await del(
|
|
86
|
+
await getProvider().del(keyOrUrl);
|
|
67
87
|
}
|
|
68
88
|
catch (error) {
|
|
69
|
-
defaultLogger.error('
|
|
89
|
+
defaultLogger.error('Object storage delete error:', error);
|
|
70
90
|
throw error;
|
|
71
91
|
}
|
|
72
92
|
},
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vercel Blob implementation of StorageProvider.
|
|
3
|
+
*
|
|
4
|
+
* Legacy backend retained during the GAP-208 Cloudflare R2 cutover so the
|
|
5
|
+
* media-upload path can fall back to Vercel Blob when R2 is not configured.
|
|
6
|
+
* Wraps @vercel/blob's put/del/list behind the provider-agnostic
|
|
7
|
+
* StorageProvider interface. Cloudflare R2 (r2.ts) is the canonical backend;
|
|
8
|
+
* this provider is removed once the Blob store is decommissioned.
|
|
9
|
+
*
|
|
10
|
+
* NOTE: distinct from vercel-blob.ts in this directory, which is the legacy
|
|
11
|
+
* Payload-style upload *plugin* still wired into apps/admin's
|
|
12
|
+
* revealui.config.ts. This file is the StorageProvider the createStorage
|
|
13
|
+
* factory returns for the 'vercel-blob' tag.
|
|
14
|
+
*
|
|
15
|
+
* Server-only. Do NOT import from client-side code or edge runtime.
|
|
16
|
+
*/
|
|
17
|
+
import type { ListOptions, ListResult, PutOptions, PutResult, StorageProvider, VercelBlobConfig } from './types.js';
|
|
18
|
+
declare class VercelBlobProvider implements StorageProvider {
|
|
19
|
+
readonly provider = "vercel-blob";
|
|
20
|
+
private readonly token;
|
|
21
|
+
constructor(config: VercelBlobConfig);
|
|
22
|
+
put(key: string, data: Blob | ArrayBuffer | Uint8Array | ReadableStream<Uint8Array>, opts?: PutOptions): Promise<PutResult>;
|
|
23
|
+
del(keyOrUrl: string): Promise<void>;
|
|
24
|
+
list(opts?: ListOptions): Promise<ListResult>;
|
|
25
|
+
}
|
|
26
|
+
export declare function createVercelBlobProvider(config: VercelBlobConfig): StorageProvider;
|
|
27
|
+
export type { VercelBlobProvider };
|
|
28
|
+
//# sourceMappingURL=vercel-blob-provider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vercel-blob-provider.d.ts","sourceRoot":"","sources":["../../src/storage/vercel-blob-provider.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAIH,OAAO,KAAK,EAEV,WAAW,EACX,UAAU,EACV,UAAU,EACV,SAAS,EACT,eAAe,EACf,gBAAgB,EACjB,MAAM,YAAY,CAAC;AAKpB,cAAM,kBAAmB,YAAW,eAAe;IACjD,QAAQ,CAAC,QAAQ,iBAAgB;IACjC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;gBAEnB,MAAM,EAAE,gBAAgB;IAW9B,GAAG,CACP,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,IAAI,GAAG,WAAW,GAAG,UAAU,GAAG,cAAc,CAAC,UAAU,CAAC,EAClE,IAAI,CAAC,EAAE,UAAU,GAChB,OAAO,CAAC,SAAS,CAAC;IAmCf,GAAG,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAOpC,IAAI,CAAC,IAAI,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,UAAU,CAAC;CAqBpD;AAED,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,gBAAgB,GAAG,eAAe,CAElF;AAED,YAAY,EAAE,kBAAkB,EAAE,CAAC"}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vercel Blob implementation of StorageProvider.
|
|
3
|
+
*
|
|
4
|
+
* Legacy backend retained during the GAP-208 Cloudflare R2 cutover so the
|
|
5
|
+
* media-upload path can fall back to Vercel Blob when R2 is not configured.
|
|
6
|
+
* Wraps @vercel/blob's put/del/list behind the provider-agnostic
|
|
7
|
+
* StorageProvider interface. Cloudflare R2 (r2.ts) is the canonical backend;
|
|
8
|
+
* this provider is removed once the Blob store is decommissioned.
|
|
9
|
+
*
|
|
10
|
+
* NOTE: distinct from vercel-blob.ts in this directory, which is the legacy
|
|
11
|
+
* Payload-style upload *plugin* still wired into apps/admin's
|
|
12
|
+
* revealui.config.ts. This file is the StorageProvider the createStorage
|
|
13
|
+
* factory returns for the 'vercel-blob' tag.
|
|
14
|
+
*
|
|
15
|
+
* Server-only. Do NOT import from client-side code or edge runtime.
|
|
16
|
+
*/
|
|
17
|
+
import { del, list, put } from '@vercel/blob';
|
|
18
|
+
import { toUint8Array } from './_helpers.js';
|
|
19
|
+
const PROVIDER_TAG = 'vercel-blob';
|
|
20
|
+
const DEFAULT_LIST_LIMIT = 1000;
|
|
21
|
+
class VercelBlobProvider {
|
|
22
|
+
provider = PROVIDER_TAG;
|
|
23
|
+
token;
|
|
24
|
+
constructor(config) {
|
|
25
|
+
if (!config.token) {
|
|
26
|
+
throw new Error('Vercel Blob provider requires VercelBlobConfig.token (the ' +
|
|
27
|
+
'BLOB_READ_WRITE_TOKEN value). For new deployments prefer Cloudflare ' +
|
|
28
|
+
'R2 (provider: "r2").');
|
|
29
|
+
}
|
|
30
|
+
this.token = config.token;
|
|
31
|
+
}
|
|
32
|
+
async put(key, data, opts) {
|
|
33
|
+
if (opts?.access === 'private') {
|
|
34
|
+
throw new Error("Vercel Blob provider does not support access: 'private' — every blob " +
|
|
35
|
+
'is publicly readable. Use Cloudflare R2 for private-access patterns.');
|
|
36
|
+
}
|
|
37
|
+
// Normalize to bytes so the result carries an accurate size regardless of
|
|
38
|
+
// input shape (ReadableStream has no length until consumed). Re-wrap in a
|
|
39
|
+
// fresh Uint8Array so the Blob part is backed by a non-shared ArrayBuffer,
|
|
40
|
+
// satisfying the DOM BlobPart type under TS strict typed-array generics.
|
|
41
|
+
const body = await toUint8Array(data);
|
|
42
|
+
const result = await put(key, new Blob([new Uint8Array(body)]), {
|
|
43
|
+
access: 'public',
|
|
44
|
+
token: this.token,
|
|
45
|
+
contentType: opts?.contentType,
|
|
46
|
+
// Keys are caller-controlled (callers pre-randomize, e.g. media/<uuid>.ext);
|
|
47
|
+
// never append a second random suffix or the returned URL won't match `key`.
|
|
48
|
+
addRandomSuffix: false,
|
|
49
|
+
});
|
|
50
|
+
// Vercel Blob has no per-object user-metadata or Cache-Control-header API
|
|
51
|
+
// (only cacheControlMaxAge); opts.metadata / opts.cacheControl are ignored
|
|
52
|
+
// by this legacy provider. R2 honors both.
|
|
53
|
+
return {
|
|
54
|
+
key,
|
|
55
|
+
url: result.url,
|
|
56
|
+
size: body.byteLength,
|
|
57
|
+
provider: PROVIDER_TAG,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
async del(keyOrUrl) {
|
|
61
|
+
// Vercel's del is idempotent (deleting an absent blob is a no-op), so this
|
|
62
|
+
// satisfies the best-effort contract. It requires the full blob URL, which
|
|
63
|
+
// is what media records persist.
|
|
64
|
+
await del(keyOrUrl, { token: this.token });
|
|
65
|
+
}
|
|
66
|
+
async list(opts) {
|
|
67
|
+
const response = await list({
|
|
68
|
+
token: this.token,
|
|
69
|
+
prefix: opts?.prefix,
|
|
70
|
+
limit: opts?.limit ?? DEFAULT_LIST_LIMIT,
|
|
71
|
+
cursor: opts?.cursor,
|
|
72
|
+
});
|
|
73
|
+
const items = response.blobs.map((blob) => ({
|
|
74
|
+
key: blob.pathname,
|
|
75
|
+
url: blob.url,
|
|
76
|
+
size: blob.size,
|
|
77
|
+
uploadedAt: blob.uploadedAt,
|
|
78
|
+
}));
|
|
79
|
+
return {
|
|
80
|
+
items,
|
|
81
|
+
cursor: response.cursor,
|
|
82
|
+
hasMore: response.hasMore,
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
export function createVercelBlobProvider(config) {
|
|
87
|
+
return new VercelBlobProvider(config);
|
|
88
|
+
}
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@revealui/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"description": "RevealUI's runtime engine — admin UI, REST API, auth, rich text, plugin system, and access control. The heart of the open-source platform.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"dependencies": {
|
|
7
|
-
"@aws-sdk/client-s3": "^3.
|
|
7
|
+
"@aws-sdk/client-s3": "^3.1053.0",
|
|
8
8
|
"@electric-sql/pglite": "^0.4.5",
|
|
9
9
|
"@lexical/clipboard": "^0.44.0",
|
|
10
10
|
"@lexical/code": "^0.44.0",
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"devDependencies": {
|
|
35
35
|
"@types/json-schema": "^7.0.15",
|
|
36
36
|
"@types/pg": "^8.20.0",
|
|
37
|
-
"@types/react": "^19.2.
|
|
37
|
+
"@types/react": "^19.2.15",
|
|
38
38
|
"@types/react-dom": "^19.2.3",
|
|
39
39
|
"@vitest/coverage-v8": "^4.1.5",
|
|
40
40
|
"expect-type": "^1.3.0",
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Vercel Blob Storage Plugin
|
|
3
|
-
*
|
|
4
|
-
* WARNING: This module is server-only.
|
|
5
|
-
* Do NOT import in client-side code or edge runtime.
|
|
6
|
-
*/
|
|
7
|
-
import type { Plugin } from '../types/index.js';
|
|
8
|
-
export interface VercelBlobStorageConfig {
|
|
9
|
-
enabled?: boolean;
|
|
10
|
-
collections?: {
|
|
11
|
-
[key: string]: boolean;
|
|
12
|
-
};
|
|
13
|
-
token?: string;
|
|
14
|
-
prefix?: string;
|
|
15
|
-
}
|
|
16
|
-
export declare function vercelBlobStorage(config: VercelBlobStorageConfig): Plugin;
|
|
17
|
-
//# sourceMappingURL=vercel-blob.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"vercel-blob.d.ts","sourceRoot":"","sources":["../../src/storage/vercel-blob.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAEhD,MAAM,WAAW,uBAAuB;IACtC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,WAAW,CAAC,EAAE;QACZ,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;KACxB,CAAC;IACF,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,uBAAuB,GAAG,MAAM,CAyFzE"}
|