graphile-presigned-url-plugin 1.15.0 → 1.15.1
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/esm/managed-upload.d.ts +12 -4
- package/esm/managed-upload.js +24 -7
- package/managed-upload.d.ts +12 -4
- package/managed-upload.js +24 -7
- package/package.json +2 -2
package/esm/managed-upload.d.ts
CHANGED
|
@@ -68,11 +68,19 @@ export interface ManagedUploadTarget {
|
|
|
68
68
|
* * a registered column names its storage module, and either a logical bucket
|
|
69
69
|
* key or the reserved default tag for its declared publicness;
|
|
70
70
|
* * an unregistered column (a bare `image`/`upload` on a database provisioned
|
|
71
|
-
* before the registry) falls back to the
|
|
72
|
-
* reserved default tag. That is a *tenant* default, not
|
|
71
|
+
* before the registry) falls back to the database's single global storage
|
|
72
|
+
* plane and the same reserved default tag. That is a *tenant* default, not
|
|
73
|
+
* an environment one.
|
|
73
74
|
*
|
|
74
|
-
*
|
|
75
|
-
*
|
|
75
|
+
* The fallback is by shape, not by scope name: a global plane is one with no
|
|
76
|
+
* entity key (`entity_table_id IS NULL`), which is what a database-wide plane is
|
|
77
|
+
* whether it registered as 'database', 'platform', or 'app'. Matching the name
|
|
78
|
+
* instead sent every tenant whose plane registers as 'database' — i.e. every
|
|
79
|
+
* database-scope plane — to STORAGE_MODULE_NOT_FOUND.
|
|
80
|
+
*
|
|
81
|
+
* A database with no global plane raises, and so does one with several: there is
|
|
82
|
+
* nowhere unambiguously tenant-owned to put the bytes, and the deployment's
|
|
83
|
+
* configured bucket is not an answer.
|
|
76
84
|
*/
|
|
77
85
|
export declare function resolveManagedUploadTarget(args: {
|
|
78
86
|
options: PresignedUrlPluginOptions;
|
package/esm/managed-upload.js
CHANGED
|
@@ -55,11 +55,19 @@ export function buildFileProjection(file, bucket, s3) {
|
|
|
55
55
|
* * a registered column names its storage module, and either a logical bucket
|
|
56
56
|
* key or the reserved default tag for its declared publicness;
|
|
57
57
|
* * an unregistered column (a bare `image`/`upload` on a database provisioned
|
|
58
|
-
* before the registry) falls back to the
|
|
59
|
-
* reserved default tag. That is a *tenant* default, not
|
|
58
|
+
* before the registry) falls back to the database's single global storage
|
|
59
|
+
* plane and the same reserved default tag. That is a *tenant* default, not
|
|
60
|
+
* an environment one.
|
|
60
61
|
*
|
|
61
|
-
*
|
|
62
|
-
*
|
|
62
|
+
* The fallback is by shape, not by scope name: a global plane is one with no
|
|
63
|
+
* entity key (`entity_table_id IS NULL`), which is what a database-wide plane is
|
|
64
|
+
* whether it registered as 'database', 'platform', or 'app'. Matching the name
|
|
65
|
+
* instead sent every tenant whose plane registers as 'database' — i.e. every
|
|
66
|
+
* database-scope plane — to STORAGE_MODULE_NOT_FOUND.
|
|
67
|
+
*
|
|
68
|
+
* A database with no global plane raises, and so does one with several: there is
|
|
69
|
+
* nowhere unambiguously tenant-owned to put the bytes, and the deployment's
|
|
70
|
+
* configured bucket is not an answer.
|
|
63
71
|
*/
|
|
64
72
|
export async function resolveManagedUploadTarget(args) {
|
|
65
73
|
const { options, withPgClient, pgSettings, databaseId, field, defaultPublicAccess } = args;
|
|
@@ -71,7 +79,7 @@ export async function resolveManagedUploadTarget(args) {
|
|
|
71
79
|
}
|
|
72
80
|
catch (err) {
|
|
73
81
|
// An unregistered column is a legitimate state (it predates the registry)
|
|
74
|
-
// and falls back to the tenant's
|
|
82
|
+
// and falls back to the tenant's global plane below. Any other
|
|
75
83
|
// failure — a broken connection, a missing registry table — is not.
|
|
76
84
|
if (err?.name === 'FileRefFieldNotRegisteredError')
|
|
77
85
|
return null;
|
|
@@ -79,15 +87,24 @@ export async function resolveManagedUploadTarget(args) {
|
|
|
79
87
|
}
|
|
80
88
|
});
|
|
81
89
|
const allConfigs = await withPgClient(null, (pgClient) => loadAllStorageModules(pgClient, databaseId));
|
|
90
|
+
const globalConfigs = allConfigs.filter((c) => c.entityTableId === null);
|
|
91
|
+
if (!binding && globalConfigs.length > 1) {
|
|
92
|
+
// Several global planes and no registry row to disambiguate: picking one is
|
|
93
|
+
// picking a tenant's bucket at random. Name them and refuse.
|
|
94
|
+
throw new Error(`STORAGE_MODULE_AMBIGUOUS: ${field.schemaName}.${field.tableName}.${field.columnName} is an ` +
|
|
95
|
+
`unregistered upload column and database ${databaseId} has ${globalConfigs.length} global ` +
|
|
96
|
+
`storage planes (scopes: ${globalConfigs.map((c) => c.scope).join(', ')}); register the column ` +
|
|
97
|
+
'so it names the plane it writes to');
|
|
98
|
+
}
|
|
82
99
|
const storageConfig = binding
|
|
83
100
|
? allConfigs.find((c) => c.id === binding.storageModuleId)
|
|
84
|
-
:
|
|
101
|
+
: globalConfigs[0];
|
|
85
102
|
if (!storageConfig) {
|
|
86
103
|
throw new Error(binding
|
|
87
104
|
? `STORAGE_MODULE_NOT_FOUND: file_ref_field ${binding.id} names storage module ` +
|
|
88
105
|
`${binding.storageModuleId}, which database ${databaseId} does not have`
|
|
89
106
|
: `STORAGE_MODULE_NOT_FOUND: ${field.schemaName}.${field.tableName}.${field.columnName} is an ` +
|
|
90
|
-
`unregistered upload column and database ${databaseId} has no
|
|
107
|
+
`unregistered upload column and database ${databaseId} has no global storage plane to ` +
|
|
91
108
|
'default to; there is no environment bucket to fall back to');
|
|
92
109
|
}
|
|
93
110
|
if (storageConfig.entityTableId !== null) {
|
package/managed-upload.d.ts
CHANGED
|
@@ -68,11 +68,19 @@ export interface ManagedUploadTarget {
|
|
|
68
68
|
* * a registered column names its storage module, and either a logical bucket
|
|
69
69
|
* key or the reserved default tag for its declared publicness;
|
|
70
70
|
* * an unregistered column (a bare `image`/`upload` on a database provisioned
|
|
71
|
-
* before the registry) falls back to the
|
|
72
|
-
* reserved default tag. That is a *tenant* default, not
|
|
71
|
+
* before the registry) falls back to the database's single global storage
|
|
72
|
+
* plane and the same reserved default tag. That is a *tenant* default, not
|
|
73
|
+
* an environment one.
|
|
73
74
|
*
|
|
74
|
-
*
|
|
75
|
-
*
|
|
75
|
+
* The fallback is by shape, not by scope name: a global plane is one with no
|
|
76
|
+
* entity key (`entity_table_id IS NULL`), which is what a database-wide plane is
|
|
77
|
+
* whether it registered as 'database', 'platform', or 'app'. Matching the name
|
|
78
|
+
* instead sent every tenant whose plane registers as 'database' — i.e. every
|
|
79
|
+
* database-scope plane — to STORAGE_MODULE_NOT_FOUND.
|
|
80
|
+
*
|
|
81
|
+
* A database with no global plane raises, and so does one with several: there is
|
|
82
|
+
* nowhere unambiguously tenant-owned to put the bytes, and the deployment's
|
|
83
|
+
* configured bucket is not an answer.
|
|
76
84
|
*/
|
|
77
85
|
export declare function resolveManagedUploadTarget(args: {
|
|
78
86
|
options: PresignedUrlPluginOptions;
|
package/managed-upload.js
CHANGED
|
@@ -61,11 +61,19 @@ function buildFileProjection(file, bucket, s3) {
|
|
|
61
61
|
* * a registered column names its storage module, and either a logical bucket
|
|
62
62
|
* key or the reserved default tag for its declared publicness;
|
|
63
63
|
* * an unregistered column (a bare `image`/`upload` on a database provisioned
|
|
64
|
-
* before the registry) falls back to the
|
|
65
|
-
* reserved default tag. That is a *tenant* default, not
|
|
64
|
+
* before the registry) falls back to the database's single global storage
|
|
65
|
+
* plane and the same reserved default tag. That is a *tenant* default, not
|
|
66
|
+
* an environment one.
|
|
66
67
|
*
|
|
67
|
-
*
|
|
68
|
-
*
|
|
68
|
+
* The fallback is by shape, not by scope name: a global plane is one with no
|
|
69
|
+
* entity key (`entity_table_id IS NULL`), which is what a database-wide plane is
|
|
70
|
+
* whether it registered as 'database', 'platform', or 'app'. Matching the name
|
|
71
|
+
* instead sent every tenant whose plane registers as 'database' — i.e. every
|
|
72
|
+
* database-scope plane — to STORAGE_MODULE_NOT_FOUND.
|
|
73
|
+
*
|
|
74
|
+
* A database with no global plane raises, and so does one with several: there is
|
|
75
|
+
* nowhere unambiguously tenant-owned to put the bytes, and the deployment's
|
|
76
|
+
* configured bucket is not an answer.
|
|
69
77
|
*/
|
|
70
78
|
async function resolveManagedUploadTarget(args) {
|
|
71
79
|
const { options, withPgClient, pgSettings, databaseId, field, defaultPublicAccess } = args;
|
|
@@ -77,7 +85,7 @@ async function resolveManagedUploadTarget(args) {
|
|
|
77
85
|
}
|
|
78
86
|
catch (err) {
|
|
79
87
|
// An unregistered column is a legitimate state (it predates the registry)
|
|
80
|
-
// and falls back to the tenant's
|
|
88
|
+
// and falls back to the tenant's global plane below. Any other
|
|
81
89
|
// failure — a broken connection, a missing registry table — is not.
|
|
82
90
|
if (err?.name === 'FileRefFieldNotRegisteredError')
|
|
83
91
|
return null;
|
|
@@ -85,15 +93,24 @@ async function resolveManagedUploadTarget(args) {
|
|
|
85
93
|
}
|
|
86
94
|
});
|
|
87
95
|
const allConfigs = await withPgClient(null, (pgClient) => (0, storage_module_cache_1.loadAllStorageModules)(pgClient, databaseId));
|
|
96
|
+
const globalConfigs = allConfigs.filter((c) => c.entityTableId === null);
|
|
97
|
+
if (!binding && globalConfigs.length > 1) {
|
|
98
|
+
// Several global planes and no registry row to disambiguate: picking one is
|
|
99
|
+
// picking a tenant's bucket at random. Name them and refuse.
|
|
100
|
+
throw new Error(`STORAGE_MODULE_AMBIGUOUS: ${field.schemaName}.${field.tableName}.${field.columnName} is an ` +
|
|
101
|
+
`unregistered upload column and database ${databaseId} has ${globalConfigs.length} global ` +
|
|
102
|
+
`storage planes (scopes: ${globalConfigs.map((c) => c.scope).join(', ')}); register the column ` +
|
|
103
|
+
'so it names the plane it writes to');
|
|
104
|
+
}
|
|
88
105
|
const storageConfig = binding
|
|
89
106
|
? allConfigs.find((c) => c.id === binding.storageModuleId)
|
|
90
|
-
:
|
|
107
|
+
: globalConfigs[0];
|
|
91
108
|
if (!storageConfig) {
|
|
92
109
|
throw new Error(binding
|
|
93
110
|
? `STORAGE_MODULE_NOT_FOUND: file_ref_field ${binding.id} names storage module ` +
|
|
94
111
|
`${binding.storageModuleId}, which database ${databaseId} does not have`
|
|
95
112
|
: `STORAGE_MODULE_NOT_FOUND: ${field.schemaName}.${field.tableName}.${field.columnName} is an ` +
|
|
96
|
-
`unregistered upload column and database ${databaseId} has no
|
|
113
|
+
`unregistered upload column and database ${databaseId} has no global storage plane to ` +
|
|
97
114
|
'default to; there is no environment bucket to fall back to');
|
|
98
115
|
}
|
|
99
116
|
if (storageConfig.entityTableId !== null) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "graphile-presigned-url-plugin",
|
|
3
|
-
"version": "1.15.
|
|
3
|
+
"version": "1.15.1",
|
|
4
4
|
"description": "Presigned URL upload plugin for PostGraphile v5 — requestUploadUrl mutation and downloadUrl computed field",
|
|
5
5
|
"author": "Constructive <developers@constructive.io>",
|
|
6
6
|
"homepage": "https://github.com/constructive-io/constructive",
|
|
@@ -62,5 +62,5 @@
|
|
|
62
62
|
"@types/node": "^22.19.11",
|
|
63
63
|
"makage": "^0.3.0"
|
|
64
64
|
},
|
|
65
|
-
"gitHead": "
|
|
65
|
+
"gitHead": "e35db12526094b515596ced2dd27a1ec8edd99d5"
|
|
66
66
|
}
|