graphile-bucket-provisioner-plugin 1.14.2 → 1.15.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/esm/plugin.js CHANGED
@@ -3,26 +3,18 @@
3
3
  *
4
4
  * Adds S3 bucket provisioning support to PostGraphile v5:
5
5
  *
6
- * 1. `provisionBucket` mutation — explicitly provision an S3 bucket for a
6
+ * 1. `provisionBucket` mutation — explicitly enqueue reconciliation for a
7
7
  * logical bucket row in the database. Reads the bucket config via RLS,
8
- * then calls BucketProvisioner to create and configure the S3 bucket.
9
- *
10
- * This plugin uses `@constructive-io/bucket-provisioner` for the actual
11
- * S3 operations (bucket creation, Block Public Access, CORS, policies,
12
- * versioning, lifecycle rules).
8
+ * then queues the same storage reconciler job used by the INSERT trigger.
13
9
  *
14
10
  * Detection: Uses the `@storageBuckets` smart tag on the codec (table).
15
11
  * The storage module generator in constructive-db sets this tag on the
16
12
  * generated buckets table via a smart comment:
17
13
  * COMMENT ON TABLE buckets IS E'@storageBuckets\nStorage buckets table';
18
14
  */
19
- import { BucketProvisioner, } from '@constructive-io/bucket-provisioner';
20
- import { Logger } from '@pgpmjs/logger';
21
15
  import { QuoteUtils } from '@pgsql/quotes';
22
16
  import { context as grafastContext, lambda, object } from 'grafast';
23
- import { recordPhysicalName as recordPhysicalBucketName } from 'graphile-storage-registry';
24
17
  import { extendSchema, gql } from 'graphile-utils';
25
- const log = new Logger('graphile-bucket-provisioner:plugin');
26
18
  // --- Storage module queries ---
27
19
  /**
28
20
  * Resolve ALL storage modules for a database (for bucket-key and ownerId-based
@@ -31,7 +23,10 @@ const log = new Logger('graphile-bucket-provisioner:plugin');
31
23
  const ALL_STORAGE_MODULES_QUERY = `
32
24
  SELECT
33
25
  sm.id,
26
+ sm.database_id,
27
+ sm.buckets_table_id,
34
28
  sm.scope,
29
+ sm.entity_field,
35
30
  sm.entity_table_id,
36
31
  bs.schema_name AS buckets_schema,
37
32
  bt.name AS buckets_table,
@@ -59,6 +54,17 @@ const ALL_STORAGE_MODULES_QUERY = `
59
54
  function runQuery(pgClient, text, values) {
60
55
  return pgClient.query(values === undefined ? { text } : { text, values });
61
56
  }
57
+ function scopeKeySelect(storageModule) {
58
+ return storageModule.entity_field === null
59
+ ? ''
60
+ : `, ${QuoteUtils.quoteIdentifier(storageModule.entity_field)} AS scope_key`;
61
+ }
62
+ function scopeKeyColumn(storageModule) {
63
+ if (storageModule.entity_field === null) {
64
+ throw new Error(`STORAGE_BUCKET_SCOPE_KEY_MISSING: storage module ${storageModule.id} has no entity field`);
65
+ }
66
+ return QuoteUtils.quoteIdentifier(storageModule.entity_field);
67
+ }
62
68
  /**
63
69
  * The explicit provisionBucket mutation's resolution: find the plane that
64
70
  * actually holds the named bucket row.
@@ -83,9 +89,9 @@ async function resolveBucketByKey(pgClient, databaseId, bucketKey, ownerId) {
83
89
  if (probe.rows.length === 0)
84
90
  continue;
85
91
  const bucketsTable = QuoteUtils.quoteQualifiedIdentifier(mod.buckets_schema, mod.buckets_table);
86
- const bucketResult = await runQuery(pgClient, `SELECT id, key, type, is_public, allowed_origins, physical_name
92
+ const bucketResult = await runQuery(pgClient, `SELECT id, key, type, is_public, allowed_origins, physical_name${scopeKeySelect(mod)}
87
93
  FROM ${bucketsTable}
88
- WHERE key = $1 AND owner_id = $2
94
+ WHERE key = $1 AND ${scopeKeyColumn(mod)} = $2
89
95
  LIMIT 1`, [bucketKey, ownerId]);
90
96
  if (bucketResult.rows.length === 0)
91
97
  return null;
@@ -96,7 +102,7 @@ async function resolveBucketByKey(pgClient, databaseId, bucketKey, ownerId) {
96
102
  const matches = [];
97
103
  for (const mod of modules.filter((m) => m.entity_table_id === null)) {
98
104
  const bucketsTable = QuoteUtils.quoteQualifiedIdentifier(mod.buckets_schema, mod.buckets_table);
99
- const bucketResult = await runQuery(pgClient, `SELECT id, key, type, is_public, allowed_origins, physical_name
105
+ const bucketResult = await runQuery(pgClient, `SELECT id, key, type, is_public, allowed_origins, physical_name${scopeKeySelect(mod)}
100
106
  FROM ${bucketsTable}
101
107
  WHERE key = $1
102
108
  LIMIT 1`, [bucketKey]);
@@ -113,43 +119,7 @@ async function resolveBucketByKey(pgClient, databaseId, bucketKey, ownerId) {
113
119
  }
114
120
  return matches[0];
115
121
  }
116
- /**
117
- * Normalize the recorded physical coordinate at the DB boundary.
118
- *
119
- * A bucket row either carries a recorded coordinate or it does not; SQL nulls
120
- * and absent columns both mean "never provisioned". Collapsing them here is
121
- * the single place that shape is interpreted — callers branch on `string`
122
- * vs `null` and never coalesce a bucket name into existence.
123
- */
124
- function storedPhysicalName(row) {
125
- return row.physical_name == null ? null : row.physical_name;
126
- }
127
122
  // --- Helpers ---
128
- /**
129
- * Resolve the connection config from the options. If the option is a lazy
130
- * getter function, call it (and cache the result).
131
- */
132
- function resolveConnection(options) {
133
- if (typeof options.connection === 'function') {
134
- const resolved = options.connection();
135
- // Cache so subsequent calls don't re-evaluate
136
- options.connection = resolved;
137
- return resolved;
138
- }
139
- return options.connection;
140
- }
141
- /**
142
- * Resolve the S3 bucket name from a logical bucket key.
143
- */
144
- function resolveBucketName(databaseId, bucketKey, options) {
145
- if (!options.resolveBucketName) {
146
- throw new Error('STORAGE_BUCKET_NAME_POLICY_MISSING: no resolveBucketName was configured, so there is ' +
147
- `no name to provision for bucket "${bucketKey}" of database ${databaseId}. ` +
148
- 'Physical bucket naming is a deployment policy; the configured s3.bucket is a ' +
149
- 'connection default and is never a tenant bucket.');
150
- }
151
- return options.resolveBucketName(databaseId, bucketKey);
152
- }
153
123
  /**
154
124
  * Resolve the database_id from the JWT context.
155
125
  */
@@ -157,72 +127,110 @@ async function resolveDatabaseId(pgClient) {
157
127
  const result = await runQuery(pgClient, `SELECT jwt_private.current_database_id() AS id`);
158
128
  return result.rows[0]?.id ?? null;
159
129
  }
160
- /**
161
- * Resolve the effective CORS allowed origins using the 3-tier hierarchy:
162
- * 1. Bucket-level allowed_origins (per-bucket override)
163
- * 2. Storage-module-level allowed_origins (per-database default)
164
- * 3. Plugin config allowedOrigins (global fallback)
165
- */
166
- function resolveAllowedOrigins(bucketOrigins, storageModuleOrigins, pluginOrigins) {
167
- if (bucketOrigins && bucketOrigins.length > 0) {
168
- return bucketOrigins;
169
- }
170
- if (storageModuleOrigins && storageModuleOrigins.length > 0) {
171
- return storageModuleOrigins;
172
- }
173
- return pluginOrigins;
174
- }
175
- /**
176
- * Build a BucketProvisioner with per-database connection overrides.
177
- */
178
- function buildProvisioner(options, storageModule, effectiveOrigins) {
179
- const connection = resolveConnection(options);
180
- const effectiveConnection = {
181
- ...connection,
182
- ...(storageModule?.endpoint ? { endpoint: storageModule.endpoint } : {}),
183
- ...(storageModule?.provider
184
- ? { provider: storageModule.provider }
185
- : {}),
130
+ async function resolveEntityContext(pgClient, storageModule) {
131
+ const result = await runQuery(pgClient, `SELECT r.entity_type, r.get_org_fn_schema, r.get_org_fn
132
+ FROM metaschema.resolve_entity_context_by_field($1::uuid, $2::uuid, $3::text) r`, [storageModule.database_id, storageModule.buckets_table_id, storageModule.entity_field]);
133
+ return result.rows[0] ?? {
134
+ entity_type: null,
135
+ get_org_fn_schema: null,
136
+ get_org_fn: null,
186
137
  };
187
- return new BucketProvisioner({
188
- connection: effectiveConnection,
189
- allowedOrigins: effectiveOrigins,
190
- });
191
138
  }
192
139
  /**
193
- * Core provisioning logic for the explicit mutation.
140
+ * Mirror the storage module generator's data_job_trigger shape from
141
+ * `packages/metaschema-generators/deploy/schemas/metaschema_generators/procedures/storage_module.sql`
142
+ * and the add_job argument assembly in
143
+ * `packages/ast-plpgsql/deploy/schemas/ast_plpgsql_helpers/procedures/triggers/job_trigger.sql`.
144
+ * A callable enqueue function beside the trigger would be the durable fix for
145
+ * this deliberate mirroring, but is out of scope here.
194
146
  */
195
- async function provisionBucketForRow(storageModule, databaseId, bucketKey, bucketType, bucketAllowedOrigins, options, s3BucketName) {
196
- const accessType = bucketType;
197
- // Resolve CORS origins using the 3-tier hierarchy
198
- const effectiveOrigins = resolveAllowedOrigins(bucketAllowedOrigins, storageModule?.allowed_origins, options.allowedOrigins);
199
- const provisioner = buildProvisioner(options, storageModule, effectiveOrigins);
200
- log.info(`Provisioning S3 bucket "${s3BucketName}" (key="${bucketKey}", type="${accessType}", ` +
201
- `origins=${JSON.stringify(effectiveOrigins)}) for database ${databaseId}`);
202
- const result = await provisioner.provision({
203
- bucketName: s3BucketName,
204
- accessType,
205
- versioning: options.versioning ?? false,
206
- publicUrlPrefix: storageModule?.public_url_prefix ?? undefined,
207
- allowedOrigins: effectiveOrigins,
208
- });
209
- log.info(`Successfully provisioned S3 bucket "${s3BucketName}" ` +
210
- `(provider=${result.provider}, blockPublicAccess=${result.blockPublicAccess})`);
211
- return result;
147
+ async function enqueueReconciliationJob(pgClient, storageModule, bucket) {
148
+ const scope = storageModule.scope;
149
+ const entityField = storageModule.entity_field;
150
+ let text;
151
+ let values;
152
+ if (entityField === null) {
153
+ text = `SELECT (app_jobs.add_job(
154
+ identifier => 'storage:provision_bucket',
155
+ payload => json_build_object(
156
+ 'id', $1::uuid,
157
+ 'scope', $2::text
158
+ ),
159
+ queue_name => 'bucket:' || $1::text,
160
+ max_attempts => 25,
161
+ priority => 0
162
+ )).id AS id`;
163
+ values = [bucket.id, scope];
164
+ }
165
+ else if (entityField === 'database_id') {
166
+ const databaseId = bucket.scope_key;
167
+ if (!databaseId) {
168
+ throw new Error(`STORAGE_BUCKET_SCOPE_KEY_MISSING: bucket ${bucket.id} has no database_id`);
169
+ }
170
+ text = `SELECT (app_jobs.add_job(
171
+ identifier => 'storage:provision_bucket',
172
+ payload => json_build_object(
173
+ 'database_id', $2::uuid,
174
+ 'id', $1::uuid,
175
+ 'scope', $3::text
176
+ ),
177
+ db_id => $2,
178
+ queue_name => 'bucket:' || $1::text,
179
+ max_attempts => 25,
180
+ priority => 0,
181
+ entity_id => $2,
182
+ organization_id => NULL,
183
+ entity_type => $3
184
+ )).id AS id`;
185
+ values = [bucket.id, databaseId, scope];
186
+ }
187
+ else if (entityField === 'owner_id') {
188
+ const ownerId = bucket.scope_key;
189
+ if (!ownerId) {
190
+ throw new Error(`STORAGE_BUCKET_SCOPE_KEY_MISSING: bucket ${bucket.id} has no owner_id`);
191
+ }
192
+ const context = await resolveEntityContext(pgClient, storageModule);
193
+ const orgFunction = context.get_org_fn_schema && context.get_org_fn
194
+ ? `${QuoteUtils.quoteQualifiedIdentifier(context.get_org_fn_schema, context.get_org_fn)}($3::text, $2::uuid)`
195
+ : 'NULL';
196
+ text = `SELECT (app_jobs.add_job(
197
+ identifier => 'storage:provision_bucket',
198
+ payload => json_build_object(
199
+ 'id', $1::uuid,
200
+ 'owner_id', $2::uuid,
201
+ 'scope', $3::text
202
+ ),
203
+ queue_name => 'bucket:' || $1::text,
204
+ max_attempts => 25,
205
+ priority => 0,
206
+ entity_id => $2,
207
+ organization_id => ${orgFunction},
208
+ entity_type => $3
209
+ )).id AS id`;
210
+ values = [bucket.id, ownerId, scope];
211
+ }
212
+ else {
213
+ throw new Error(`STORAGE_BUCKET_ENTITY_FIELD_UNSUPPORTED: ${entityField}`);
214
+ }
215
+ const result = await runQuery(pgClient, text, values);
216
+ const jobId = result.rows[0]?.id;
217
+ if (!jobId) {
218
+ throw new Error(`STORAGE_BUCKET_JOB_ID_MISSING: bucket ${bucket.id}`);
219
+ }
220
+ return jobId;
212
221
  }
213
222
  // --- Plugin factory ---
214
223
  /**
215
224
  * Creates the bucket provisioner plugin.
216
225
  *
217
- * This plugin provides one provisioning pathway:
226
+ * This plugin provides one reconciliation pathway:
218
227
  *
219
228
  * 1. **Explicit `provisionBucket` mutation** — Call this mutation with a
220
- * bucket key to provision (or re-provision) the S3 bucket. Protected
221
- * by RLS on the buckets table.
229
+ * bucket key to enqueue reconciliation (or re-reconciliation) for the
230
+ * bucket. Protected by RLS on the buckets table.
222
231
  *
223
- * @param options - Plugin configuration (S3 credentials, CORS origins, naming)
224
232
  */
225
- export function createBucketProvisionerPlugin(options) {
233
+ export function createBucketProvisionerPlugin() {
226
234
  // The extendSchema plugin adds the explicit provisionBucket mutation
227
235
  const mutationPlugin = extendSchema(() => ({
228
236
  typeDefs: gql `
@@ -237,26 +245,22 @@ export function createBucketProvisionerPlugin(options) {
237
245
  }
238
246
 
239
247
  type ProvisionBucketPayload {
240
- """Whether provisioning succeeded"""
241
- success: Boolean!
242
- """The S3 bucket name that was provisioned"""
243
- bucketName: String!
244
- """The access type applied"""
245
- accessType: String!
246
- """The storage provider used"""
247
- provider: String!
248
- """The S3 endpoint (null for AWS S3 default)"""
249
- endpoint: String
250
- """Error message if provisioning failed"""
251
- error: String
248
+ """The logical bucket row that was queued for reconciliation."""
249
+ bucketId: UUID!
250
+ bucketKey: String!
251
+ """The physical bucket name already recorded, or null when reconciliation has not completed."""
252
+ physicalName: String
253
+ """The reconciler job enqueued to provision this bucket."""
254
+ jobId: UUID!
252
255
  }
253
256
 
254
257
  extend type Mutation {
255
258
  """
256
- Provision an S3 bucket for a logical bucket in the database.
257
- Reads the bucket config via RLS, then creates and configures
258
- the S3 bucket with the appropriate privacy policies, CORS rules,
259
- and lifecycle settings.
259
+ Reconcile an S3 bucket for a logical bucket in the database.
260
+ Reads the bucket config via RLS, then enqueues the same
261
+ storage:provision_bucket job used by the INSERT trigger. This is
262
+ idempotent for an already-reconciled bucket; enqueue failures become
263
+ GraphQL errors.
260
264
  """
261
265
  provisionBucket(
262
266
  input: ProvisionBucketInput!
@@ -292,38 +296,13 @@ export function createBucketProvisionerPlugin(options) {
292
296
  throw new Error('BUCKET_NOT_FOUND');
293
297
  }
294
298
  const { storageModule, bucket } = resolution;
295
- const bucketsTable = QuoteUtils.quoteQualifiedIdentifier(storageModule.buckets_schema, storageModule.buckets_table);
296
- // First provision mints a name; afterwards the stored coordinate
297
- // is authoritative and the naming hook is never consulted again.
298
- const recorded = storedPhysicalName(bucket);
299
- const s3BucketName = recorded === null
300
- ? resolveBucketName(databaseId, bucket.key, options)
301
- : recorded;
302
- try {
303
- const result = await provisionBucketForRow(storageModule, databaseId, bucket.key, bucket.type, bucket.allowed_origins, options, s3BucketName);
304
- // Record the exact provisioned name on the source row.
305
- await withPgClient(null, (client) => recordPhysicalBucketName((query) => runQuery(client, query.text, query.values), bucketsTable, bucket.id, result.bucketName));
306
- log.info(`Recorded physical_name="${result.bucketName}" on bucket ${bucket.id}`);
307
- return {
308
- success: true,
309
- bucketName: result.bucketName,
310
- accessType: result.accessType,
311
- provider: result.provider,
312
- endpoint: result.endpoint,
313
- error: null,
314
- };
315
- }
316
- catch (err) {
317
- log.error(`Failed to provision bucket "${bucketKey}": ${err.message}`);
318
- return {
319
- success: false,
320
- bucketName: s3BucketName,
321
- accessType: bucket.type,
322
- provider: resolveConnection(options).provider,
323
- endpoint: resolveConnection(options).endpoint ?? null,
324
- error: err.message,
325
- };
326
- }
299
+ const jobId = await enqueueReconciliationJob(pgClient, storageModule, bucket);
300
+ return {
301
+ bucketId: bucket.id,
302
+ bucketKey: bucket.key,
303
+ physicalName: bucket.physical_name,
304
+ jobId,
305
+ };
327
306
  });
328
307
  });
329
308
  },
package/esm/preset.d.ts CHANGED
@@ -5,36 +5,8 @@
5
5
  * in PostGraphile. Wraps the main plugin with sensible defaults.
6
6
  */
7
7
  import type { GraphileConfig } from 'graphile-config';
8
- import type { BucketProvisionerPluginOptions } from './types';
9
8
  /**
10
- * Creates a preset that includes the bucket provisioner plugin with the given options.
11
- *
12
- * @example
13
- * ```typescript
14
- * import { BucketProvisionerPreset } from 'graphile-bucket-provisioner-plugin';
15
- * import { getEnvOptions } from '@constructive-io/graphql-env';
16
- *
17
- * // Use a lazy getter so env vars are read at runtime, not import time
18
- * function getConnection() {
19
- * const { cdn } = getEnvOptions();
20
- * return {
21
- * provider: cdn?.provider || 'minio',
22
- * region: cdn?.awsRegion || 'us-east-1',
23
- * endpoint: cdn?.endpoint || 'http://minio:9000',
24
- * accessKeyId: cdn?.awsAccessKey!,
25
- * secretAccessKey: cdn?.awsSecretKey!,
26
- * };
27
- * }
28
- *
29
- * const preset = {
30
- * extends: [
31
- * BucketProvisionerPreset({
32
- * connection: getConnection, // pass function ref, NOT getConnection()
33
- * allowedOrigins: ['https://app.example.com'],
34
- * }),
35
- * ],
36
- * };
37
- * ```
9
+ * Creates a preset that includes the bucket reconciliation plugin.
38
10
  */
39
- export declare function BucketProvisionerPreset(options: BucketProvisionerPluginOptions): GraphileConfig.Preset;
11
+ export declare function BucketProvisionerPreset(): GraphileConfig.Preset;
40
12
  export default BucketProvisionerPreset;
package/esm/preset.js CHANGED
@@ -6,38 +6,11 @@
6
6
  */
7
7
  import { createBucketProvisionerPlugin } from './plugin';
8
8
  /**
9
- * Creates a preset that includes the bucket provisioner plugin with the given options.
10
- *
11
- * @example
12
- * ```typescript
13
- * import { BucketProvisionerPreset } from 'graphile-bucket-provisioner-plugin';
14
- * import { getEnvOptions } from '@constructive-io/graphql-env';
15
- *
16
- * // Use a lazy getter so env vars are read at runtime, not import time
17
- * function getConnection() {
18
- * const { cdn } = getEnvOptions();
19
- * return {
20
- * provider: cdn?.provider || 'minio',
21
- * region: cdn?.awsRegion || 'us-east-1',
22
- * endpoint: cdn?.endpoint || 'http://minio:9000',
23
- * accessKeyId: cdn?.awsAccessKey!,
24
- * secretAccessKey: cdn?.awsSecretKey!,
25
- * };
26
- * }
27
- *
28
- * const preset = {
29
- * extends: [
30
- * BucketProvisionerPreset({
31
- * connection: getConnection, // pass function ref, NOT getConnection()
32
- * allowedOrigins: ['https://app.example.com'],
33
- * }),
34
- * ],
35
- * };
36
- * ```
9
+ * Creates a preset that includes the bucket reconciliation plugin.
37
10
  */
38
- export function BucketProvisionerPreset(options) {
11
+ export function BucketProvisionerPreset() {
39
12
  return {
40
- plugins: [createBucketProvisionerPlugin(options)],
13
+ plugins: [createBucketProvisionerPlugin()],
41
14
  };
42
15
  }
43
16
  export default BucketProvisionerPreset;
package/esm/types.d.ts CHANGED
@@ -1,50 +1,6 @@
1
1
  /**
2
2
  * Types for the bucket provisioner plugin.
3
3
  */
4
- import type { BucketAccessType, ProvisionResult, StorageConnectionConfig, StorageProvider } from '@constructive-io/bucket-provisioner';
5
- export type { BucketAccessType, ProvisionResult, StorageConnectionConfig, StorageProvider };
6
- /**
7
- * S3 connection configuration or a lazy getter that returns it on first use.
8
- *
9
- * When a function is provided, it will only be called when the first
10
- * provisioning operation actually needs the S3 client — avoiding eager
11
- * env-var reads and S3Client creation at module import time.
12
- */
13
- export type ConnectionConfigOrGetter = StorageConnectionConfig | (() => StorageConnectionConfig);
14
- /**
15
- * Function to derive the actual S3 bucket name from a logical bucket key.
16
- *
17
- * @param databaseId - The metaschema database UUID
18
- * @param bucketKey - The logical bucket key from the database (e.g., "public", "private")
19
- * @returns The S3 bucket name to create/configure
20
- */
21
- export type BucketNameResolver = (databaseId: string, bucketKey: string) => string;
22
- /**
23
- * Plugin options for the bucket provisioner plugin.
24
- */
25
- export interface BucketProvisionerPluginOptions {
26
- /**
27
- * S3 connection configuration (credentials, endpoint, provider).
28
- * Can be a concrete object or a lazy getter function.
29
- */
30
- connection: ConnectionConfigOrGetter;
31
- /**
32
- * Allowed origins for CORS rules on provisioned buckets.
33
- * These are the domains where your app runs (e.g., ["https://app.example.com"]).
34
- * Required for browser-based presigned URL uploads.
35
- */
36
- allowedOrigins: string[];
37
- /**
38
- * Optional custom function to derive S3 bucket names from logical bucket keys.
39
- * Naming is a deployment policy and must be supplied by the caller.
40
- */
41
- resolveBucketName?: BucketNameResolver;
42
- /**
43
- * Whether to enable versioning on provisioned buckets.
44
- * Default: false
45
- */
46
- versioning?: boolean;
47
- }
48
4
  /**
49
5
  * Input for the provisionBucket mutation.
50
6
  */
@@ -58,19 +14,15 @@ export interface ProvisionBucketInput {
58
14
  ownerId?: string;
59
15
  }
60
16
  /**
61
- * Result of the provisionBucket mutation.
17
+ * Result of the provisionBucket reconciliation enqueue mutation.
62
18
  */
63
19
  export interface ProvisionBucketPayload {
64
- /** Whether provisioning succeeded */
65
- success: boolean;
66
- /** The S3 bucket name that was provisioned */
67
- bucketName: string;
68
- /** The access type applied */
69
- accessType: string;
70
- /** The storage provider used */
71
- provider: string;
72
- /** The S3 endpoint (null for AWS S3 default) */
73
- endpoint: string | null;
74
- /** Error message if provisioning failed */
75
- error: string | null;
20
+ /** The logical bucket row queued for reconciliation */
21
+ bucketId: string;
22
+ /** The logical bucket key */
23
+ bucketKey: string;
24
+ /** The physical name already recorded, or null while reconciliation is pending */
25
+ physicalName: string | null;
26
+ /** The queued reconciler job */
27
+ jobId: string;
76
28
  }
package/index.d.ts CHANGED
@@ -1,35 +1,9 @@
1
1
  /**
2
2
  * Bucket Provisioner Plugin for PostGraphile v5
3
3
  *
4
- * Provides an explicit `provisionBucket` mutation for PostGraphile v5.
5
- *
6
- * @example
7
- * ```typescript
8
- * import { BucketProvisionerPreset } from 'graphile-bucket-provisioner-plugin';
9
- * import { getEnvOptions } from '@constructive-io/graphql-env';
10
- *
11
- * // Use a lazy getter so env vars are read at runtime, not import time
12
- * function getConnection() {
13
- * const { cdn } = getEnvOptions();
14
- * return {
15
- * provider: cdn?.provider || 'minio',
16
- * region: cdn?.awsRegion || 'us-east-1',
17
- * endpoint: cdn?.endpoint || 'http://minio:9000',
18
- * accessKeyId: cdn?.awsAccessKey!,
19
- * secretAccessKey: cdn?.awsSecretKey!,
20
- * };
21
- * }
22
- *
23
- * const preset = {
24
- * extends: [
25
- * BucketProvisionerPreset({
26
- * connection: getConnection, // pass function ref, NOT getConnection()
27
- * allowedOrigins: ['https://app.example.com'],
28
- * }),
29
- * ],
30
- * };
31
- * ```
4
+ * Provides an explicit `provisionBucket` reconciliation enqueue mutation for
5
+ * PostGraphile v5.
32
6
  */
33
7
  export { BucketProvisionerPlugin, createBucketProvisionerPlugin } from './plugin';
34
8
  export { BucketProvisionerPreset } from './preset';
35
- export type { BucketAccessType, BucketNameResolver, BucketProvisionerPluginOptions, ConnectionConfigOrGetter, ProvisionBucketInput, ProvisionBucketPayload, ProvisionResult, StorageConnectionConfig, StorageProvider, } from './types';
9
+ export type { ProvisionBucketInput, ProvisionBucketPayload, } from './types';
package/index.js CHANGED
@@ -2,34 +2,8 @@
2
2
  /**
3
3
  * Bucket Provisioner Plugin for PostGraphile v5
4
4
  *
5
- * Provides an explicit `provisionBucket` mutation for PostGraphile v5.
6
- *
7
- * @example
8
- * ```typescript
9
- * import { BucketProvisionerPreset } from 'graphile-bucket-provisioner-plugin';
10
- * import { getEnvOptions } from '@constructive-io/graphql-env';
11
- *
12
- * // Use a lazy getter so env vars are read at runtime, not import time
13
- * function getConnection() {
14
- * const { cdn } = getEnvOptions();
15
- * return {
16
- * provider: cdn?.provider || 'minio',
17
- * region: cdn?.awsRegion || 'us-east-1',
18
- * endpoint: cdn?.endpoint || 'http://minio:9000',
19
- * accessKeyId: cdn?.awsAccessKey!,
20
- * secretAccessKey: cdn?.awsSecretKey!,
21
- * };
22
- * }
23
- *
24
- * const preset = {
25
- * extends: [
26
- * BucketProvisionerPreset({
27
- * connection: getConnection, // pass function ref, NOT getConnection()
28
- * allowedOrigins: ['https://app.example.com'],
29
- * }),
30
- * ],
31
- * };
32
- * ```
5
+ * Provides an explicit `provisionBucket` reconciliation enqueue mutation for
6
+ * PostGraphile v5.
33
7
  */
34
8
  Object.defineProperty(exports, "__esModule", { value: true });
35
9
  exports.BucketProvisionerPreset = exports.createBucketProvisionerPlugin = exports.BucketProvisionerPlugin = void 0;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "graphile-bucket-provisioner-plugin",
3
- "version": "1.14.2",
3
+ "version": "1.15.0",
4
4
  "description": "Bucket provisioning plugin for PostGraphile v5 — explicitly provisions S3 buckets via a GraphQL mutation",
5
5
  "author": "Constructive <developers@constructive.io>",
6
6
  "homepage": "https://github.com/constructive-io/constructive",
@@ -40,7 +40,7 @@
40
40
  "url": "https://github.com/constructive-io/constructive/issues"
41
41
  },
42
42
  "dependencies": {
43
- "@constructive-io/bucket-provisioner": "^0.26.0",
43
+ "@constructive-io/bucket-provisioner": "^0.27.0",
44
44
  "@pgpmjs/logger": "^2.25.0",
45
45
  "@pgsql/quotes": "^18.2.4",
46
46
  "graphile-storage-registry": "^0.1.1"
@@ -58,5 +58,5 @@
58
58
  "@types/node": "^22.19.11",
59
59
  "makage": "^0.3.0"
60
60
  },
61
- "gitHead": "ec6638bb4a06080919cee7e9ebf48306a2535f2f"
61
+ "gitHead": "77d58bb93796be91d4e00156bb6847392584d3bc"
62
62
  }