@develit-io/backend-sdk 5.13.0 → 5.14.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/dist/index.cjs CHANGED
@@ -3,6 +3,7 @@
3
3
  const drizzleOrm = require('drizzle-orm');
4
4
  const pgCore = require('drizzle-orm/pg-core');
5
5
  const sqliteCore = require('drizzle-orm/sqlite-core');
6
+ const text = require('@std/text');
6
7
  require('http-status-codes');
7
8
  const z = require('zod/v4/core');
8
9
  const h3 = require('h3');
@@ -51,6 +52,277 @@ const basePostgres = {
51
52
  }).default(drizzleOrm.sql`null`)
52
53
  };
53
54
 
55
+ class Infrastructure {
56
+ project;
57
+ environment;
58
+ sst;
59
+ constructor({
60
+ project,
61
+ environment,
62
+ sst
63
+ }) {
64
+ this.project = project;
65
+ this.environment = environment;
66
+ this.sst = sst;
67
+ }
68
+ // TODO(Pookensivee): Make tests for this util
69
+ composeBindingName({
70
+ resource,
71
+ resourceName,
72
+ bindingName
73
+ }) {
74
+ const convertedBindingName = bindingName ? text.toSnakeCase(bindingName) : `${text.toSnakeCase(resourceName)}_${resource}`;
75
+ return convertedBindingName.toUpperCase();
76
+ }
77
+ // TODO(Pookensivee): Make tests for this util
78
+ composeResourceName({ resourceName }) {
79
+ return `${this.project}-${resourceName}-${this.environment}`;
80
+ }
81
+ // TODO(Pookensivee): Make tests for this util
82
+ composeKvArguments({ resourceName }) {
83
+ return {
84
+ transform: {
85
+ namespace: {
86
+ title: resourceName
87
+ }
88
+ }
89
+ };
90
+ }
91
+ // TODO(Pookensivee): Make tests for this util
92
+ composeD1Arguments({ resourceName }) {
93
+ return {
94
+ transform: {
95
+ database: {
96
+ name: resourceName,
97
+ primaryLocationHint: "weur"
98
+ }
99
+ }
100
+ };
101
+ }
102
+ // TODO(Pookensivee): Make tests for this util
103
+ composeQueueArguments({
104
+ resourceName,
105
+ deliveryDelay = 5,
106
+ messageRetentionPeriod = 259200
107
+ }) {
108
+ return {
109
+ transform: {
110
+ queue: {
111
+ queueName: resourceName,
112
+ settings: {
113
+ deliveryDelay,
114
+ messageRetentionPeriod
115
+ }
116
+ }
117
+ }
118
+ };
119
+ }
120
+ // TODO(Pookensivee): Make tests for this util
121
+ composeR2Arguments({
122
+ resourceName,
123
+ storageClass = "Standard"
124
+ }) {
125
+ return {
126
+ transform: {
127
+ bucket: {
128
+ name: resourceName,
129
+ jurisdiction: "eu",
130
+ location: "weur",
131
+ storageClass
132
+ }
133
+ }
134
+ };
135
+ }
136
+ // TODO: Solve the circular dependency on post infrastructure deploy script
137
+ // TODO: Cannot assign a queue as a producer, work around: https://developers.cloudflare.com/workers/wrangler/commands/#queues-consumer-add-script-name
138
+ // async composeWorkerArguments({
139
+ // resourceName,
140
+ // path,
141
+ // bindings = [],
142
+ // }: {
143
+ // resourceName: string
144
+ // path: string
145
+ // bindings?: Input<Kv | D1 | Queue | Worker | Bucket>[]
146
+ // }) {
147
+ // const workerConfig = this.loadWorkerConfig({ path })
148
+ // const environmentVariables = this.extractEnvironmentVariables({ workerConfigEnv:
149
+ // workerConfig.env as WorkerConfigEnv
150
+ // })
151
+ // // TODO: Fix this
152
+ // if (
153
+ // 'SERVICE_CONFIG' in environmentVariables &&
154
+ // typeof environmentVariables.SERVICE_CONFIG === 'object'
155
+ // ) {
156
+ // environmentVariables.SERVICE_CONFIG = JSON.stringify(environmentVariables.SERVICE_CONFIG)
157
+ // }
158
+ // // TODO: Fix this
159
+ // if (
160
+ // 'EMAIL_SENDER' in environmentVariables &&
161
+ // typeof environmentVariables.EMAIL_SENDER === 'object'
162
+ // ) {
163
+ // environmentVariables.EMAIL_SENDER = JSON.stringify(environmentVariables.EMAIL_SENDER)
164
+ // }
165
+ // return {
166
+ // handler: join(path, './src/index.ts'),
167
+ // environment: Object.entries(environmentVariables).reduce((acc, [key, value]) => ({
168
+ // ...acc,
169
+ // [key]: String(value)
170
+ // }), {} as Record<string, string>),
171
+ // link: bindings,
172
+ // transform: {
173
+ // worker: {
174
+ // scriptName: this.composeResourceName({ resourceName }),
175
+ // compatibilityDate: '2025-06-04',
176
+ // compatibilityFlags: ['nodejs_compat'],
177
+ // observability: {
178
+ // enabled: true,
179
+ // headSamplingRate: 1,
180
+ // logs: {
181
+ // // Check whether this disables logs completely or just invocation ones
182
+ // enabled: false,
183
+ // invocationLogs: false,
184
+ // },
185
+ // },
186
+ // },
187
+ // }
188
+ // } satisfies Partial<WorkerArgs>
189
+ // }
190
+ // loadWorkerConfig({ path }: { path: string }) {
191
+ // const workerConfigFile = readFileSync(
192
+ // join(path, './wrangler.jsonc'),
193
+ // 'utf-8',
194
+ // )
195
+ // TODO: Use parse from comment-json
196
+ // const jsonString = workerConfigFile
197
+ // .replace(/\/\*[\s\S]*?\*\//g, '')
198
+ // .replace(/\/\/.*$/gm, '')
199
+ // return JSON.parse(jsonString)
200
+ // }
201
+ // extractEnvironmentVariables({
202
+ // workerConfigEnv,
203
+ // }: {
204
+ // workerConfigEnv: WorkerConfigEnv
205
+ // }) {
206
+ // if (typeof this.environment === 'number') {
207
+ // return { ...workerConfigEnv.dev.vars, ENVIRONMENT: this.environment }
208
+ // }
209
+ // return { ...workerConfigEnv[this.environment].vars }
210
+ // }
211
+ /**
212
+ * Creates an instance of Cloudflare KV.
213
+ */
214
+ kv(options) {
215
+ const { resourceName, bindingName } = options;
216
+ return new this.sst.cloudflare.Kv(
217
+ `${this.composeBindingName({ resource: "kv", resourceName, bindingName })}`,
218
+ this.composeKvArguments({
219
+ resourceName: this.composeResourceName({ resourceName })
220
+ })
221
+ );
222
+ }
223
+ /**
224
+ * Creates an instance of Cloudflare D1.
225
+ */
226
+ d1(options) {
227
+ const { resourceName, bindingName } = options;
228
+ return new this.sst.cloudflare.D1(
229
+ `${this.composeBindingName({ resource: "d1", resourceName, bindingName })}`,
230
+ this.composeD1Arguments({
231
+ resourceName: this.composeResourceName({ resourceName })
232
+ })
233
+ );
234
+ }
235
+ /**
236
+ * Creates an instance of Cloudflare Queue.
237
+ */
238
+ queue(options) {
239
+ const { resourceName, bindingName, deliveryDelay, messageRetentionPeriod } = options;
240
+ return new this.sst.cloudflare.Queue(
241
+ `${this.composeBindingName({ resource: "queue", resourceName, bindingName })}`,
242
+ this.composeQueueArguments({
243
+ resourceName: this.composeResourceName({ resourceName }),
244
+ deliveryDelay,
245
+ messageRetentionPeriod
246
+ })
247
+ );
248
+ }
249
+ /**
250
+ * Creates an instance of Cloudflare R2.
251
+ */
252
+ r2(options) {
253
+ const { resourceName, bindingName, storageClass } = options;
254
+ return new this.sst.cloudflare.Bucket(
255
+ `${this.composeBindingName({ resource: "r2", resourceName, bindingName })}`,
256
+ this.composeR2Arguments({
257
+ resourceName: this.composeResourceName({ resourceName }),
258
+ storageClass
259
+ })
260
+ );
261
+ }
262
+ // TODO: Solve the circular dependency on post infrastructure deploy script
263
+ // async worker({
264
+ // resourceName,
265
+ // bindingName,
266
+ // path,
267
+ // bindings = [],
268
+ // }: {
269
+ // resourceName: string
270
+ // bindingName: string
271
+ // path: string
272
+ // bindings?: Input<Kv | D1 | Queue | Worker | Bucket>[]
273
+ // }) {
274
+ // return new sst.cloudflare.Worker(
275
+ // this.composeBindingName({
276
+ // resource: 'worker',
277
+ // bindingName,
278
+ // resourceName,
279
+ // }),
280
+ // await this.composeWorkerArguments({
281
+ // resourceName: this.composeResourceName({ resourceName }),
282
+ // path,
283
+ // bindings,
284
+ // }),
285
+ // )
286
+ // }
287
+ // async service({
288
+ // resourceName,
289
+ // bindingName,
290
+ // path,
291
+ // bindings = [],
292
+ // }: {
293
+ // resourceName: string
294
+ // bindingName?: string
295
+ // path?: string
296
+ // bindings?: Input<Kv | D1 | Queue | Worker | Bucket>[]
297
+ // }) {
298
+ // return this.worker({
299
+ // resourceName: `${this.project}-${resourceName}-service-${this.environment}`,
300
+ // bindingName: this.composeBindingName({
301
+ // resource: 'service',
302
+ // bindingName,
303
+ // resourceName,
304
+ // }),
305
+ // path: `${path ?? `./services/${resourceName}`}`,
306
+ // bindings,
307
+ // })
308
+ // }
309
+ // // TODO: Add name
310
+ // async orchestrator({
311
+ // path,
312
+ // bindings = [],
313
+ // }: {
314
+ // path?: string
315
+ // bindings?: Input<Kv | D1 | Queue | Worker | Bucket>[]
316
+ // }) {
317
+ // return this.worker({
318
+ // resourceName: `${this.project}-gateway-${this.environment}`,
319
+ // bindingName: 'GATEWAY',
320
+ // path: `${path ?? `./apps/gateway`}`,
321
+ // bindings,
322
+ // })
323
+ // }
324
+ }
325
+
54
326
  const ibanZodSchema = new z__namespace.$ZodString({
55
327
  type: "string",
56
328
  checks: [
@@ -679,6 +951,7 @@ function develitWorker(Worker) {
679
951
  }
680
952
 
681
953
  exports.DatabaseTransaction = DatabaseTransaction;
954
+ exports.Infrastructure = Infrastructure;
682
955
  exports.RPCResponse = RPCResponse;
683
956
  exports.action = action;
684
957
  exports.base = base;
package/dist/index.d.cts CHANGED
@@ -24,6 +24,147 @@ declare const basePostgres: {
24
24
  deletedAt: drizzle_orm.HasDefault<drizzle_orm_pg_core.PgTimestampBuilderInitial<"deleted_at">>;
25
25
  };
26
26
 
27
+ type Project = 'creditio' | 'fp' | 'mdm' | 'moneio' | 'txs';
28
+
29
+ type Resource = 'kv' | 'd1' | 'queue' | 'r2' | 'worker' | 'service' | 'orchestrator';
30
+
31
+ type Environment = number | 'dev' | 'test' | 'staging' | 'production';
32
+
33
+ interface SstInstance {
34
+ cloudflare: {
35
+ [key: string]: any;
36
+ };
37
+ }
38
+ declare class Infrastructure {
39
+ private project;
40
+ private environment;
41
+ private sst;
42
+ constructor({ project, environment, sst, }: {
43
+ project: Project;
44
+ environment: Environment;
45
+ sst: SstInstance;
46
+ });
47
+ composeBindingName({ resource, resourceName, bindingName, }: {
48
+ resource: Resource;
49
+ resourceName: string;
50
+ bindingName?: string;
51
+ }): string;
52
+ composeResourceName({ resourceName }: {
53
+ resourceName: string;
54
+ }): string;
55
+ composeKvArguments({ resourceName }: {
56
+ resourceName: string;
57
+ }): {
58
+ transform: {
59
+ namespace: {
60
+ title: string;
61
+ };
62
+ };
63
+ };
64
+ composeD1Arguments({ resourceName }: {
65
+ resourceName: string;
66
+ }): {
67
+ transform: {
68
+ database: {
69
+ name: string;
70
+ primaryLocationHint: string;
71
+ };
72
+ };
73
+ };
74
+ composeQueueArguments({ resourceName, deliveryDelay, messageRetentionPeriod, }: {
75
+ resourceName: string;
76
+ deliveryDelay?: number;
77
+ messageRetentionPeriod?: number;
78
+ }): {
79
+ transform: {
80
+ queue: {
81
+ queueName: string;
82
+ settings: {
83
+ deliveryDelay: number;
84
+ messageRetentionPeriod: number;
85
+ };
86
+ };
87
+ };
88
+ };
89
+ composeR2Arguments({ resourceName, storageClass, }: {
90
+ resourceName: string;
91
+ storageClass?: 'Standard' | 'InfrequentAccess';
92
+ }): {
93
+ transform: {
94
+ bucket: {
95
+ name: string;
96
+ jurisdiction: string;
97
+ location: string;
98
+ storageClass: "Standard" | "InfrequentAccess";
99
+ };
100
+ };
101
+ };
102
+ /**
103
+ * Creates an instance of Cloudflare KV.
104
+ */
105
+ kv(options: {
106
+ /**
107
+ * Name of the KV. Do not include the 'kv' prefix.
108
+ */
109
+ resourceName: string;
110
+ /**
111
+ * Name of the KV's binding. If not set, it is generated automatically based on the provided name.
112
+ */
113
+ bindingName?: string;
114
+ }): any;
115
+ /**
116
+ * Creates an instance of Cloudflare D1.
117
+ */
118
+ d1(options: {
119
+ /**
120
+ * Name of the D1. Do not include the 'd1' prefix.
121
+ */
122
+ resourceName: string;
123
+ /**
124
+ * Name of the D1's binding. If not set, it is generated automatically based on the provided name.
125
+ */
126
+ bindingName?: string;
127
+ }): any;
128
+ /**
129
+ * Creates an instance of Cloudflare Queue.
130
+ */
131
+ queue(options: {
132
+ /**
133
+ * Name of the Queue. Do not include the 'queue' prefix.
134
+ */
135
+ resourceName: string;
136
+ /**
137
+ * Name of the Queue's binding. If not set, it is generated automatically based on the provided name.
138
+ */
139
+ bindingName?: string;
140
+ /**
141
+ * The number of seconds to delay delivery of messages to the queue.
142
+ */
143
+ deliveryDelay?: number;
144
+ /**
145
+ * The number of seconds a message will remain in the queue before being deleted.
146
+ */
147
+ messageRetentionPeriod?: number;
148
+ }): any;
149
+ /**
150
+ * Creates an instance of Cloudflare R2.
151
+ */
152
+ r2(options: {
153
+ /**
154
+ * Name of the R2. Do not include the 'r2' prefix.
155
+ */
156
+ resourceName: string;
157
+ /**
158
+ * Name of the R2's binding. If not set, it is generated automatically based on the provided name.
159
+ */
160
+ bindingName?: string;
161
+ /**
162
+ * The storage class for the R2 bucket.
163
+ */
164
+ storageClass?: 'Standard' | 'InfrequentAccess';
165
+ }): any;
166
+ }
167
+
27
168
  type InternalErrorResponseStatus = Exclude<StatusCodes, 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207>;
28
169
  interface InternalError {
29
170
  status: InternalErrorResponseStatus;
@@ -406,5 +547,5 @@ interface WithRetryCounterOptions {
406
547
  type AsyncMethod<TArgs extends unknown[] = unknown[], TResult = unknown> = (...args: TArgs) => Promise<TResult>;
407
548
  declare function cloudflareQueue<TArgs extends unknown[] = unknown[], TResult = unknown>(options: WithRetryCounterOptions): (target: unknown, propertyKey: string | symbol, descriptor: TypedPropertyDescriptor<AsyncMethod<TArgs, TResult>>) => void;
408
549
 
409
- export { DatabaseTransaction, RPCResponse, action, base, basePostgres, calculateExponentialBackoff, cloudflareQueue, createAuditLogWriter, createInternalError, defineCommand, derivePortFromId, develitWorker, durableObjectNamespaceIdFromName, first, firstOrError, getD1Credentials, getD1DatabaseIdFromWrangler, getDrizzleD1Config, getDrizzlePgConfig, getPgCredentials, getPgDatabaseIdFromWrangler, getPgLocalConnectionString, handleAction, handleActionResponse, ibanZodSchema, isInternalError, paginationQuerySchema, paginationSchema, service, swiftZodSchema, useResult, useResultSync, uuidv4 };
410
- export type { ActionExecution, ActionHandlerOptions, AuditLogWriter, Command, CommandLogPayload, DevelitWorkerMethods, GatewayResponse, IRPCResponse, IncludeRelation, InferResultType, InternalError, InternalErrorResponseStatus, ValidatedInput };
550
+ export { DatabaseTransaction, Infrastructure, RPCResponse, action, base, basePostgres, calculateExponentialBackoff, cloudflareQueue, createAuditLogWriter, createInternalError, defineCommand, derivePortFromId, develitWorker, durableObjectNamespaceIdFromName, first, firstOrError, getD1Credentials, getD1DatabaseIdFromWrangler, getDrizzleD1Config, getDrizzlePgConfig, getPgCredentials, getPgDatabaseIdFromWrangler, getPgLocalConnectionString, handleAction, handleActionResponse, ibanZodSchema, isInternalError, paginationQuerySchema, paginationSchema, service, swiftZodSchema, useResult, useResultSync, uuidv4 };
551
+ export type { ActionExecution, ActionHandlerOptions, AuditLogWriter, Command, CommandLogPayload, DevelitWorkerMethods, Environment, GatewayResponse, IRPCResponse, IncludeRelation, InferResultType, InternalError, InternalErrorResponseStatus, Project, ValidatedInput };
package/dist/index.d.mts CHANGED
@@ -24,6 +24,147 @@ declare const basePostgres: {
24
24
  deletedAt: drizzle_orm.HasDefault<drizzle_orm_pg_core.PgTimestampBuilderInitial<"deleted_at">>;
25
25
  };
26
26
 
27
+ type Project = 'creditio' | 'fp' | 'mdm' | 'moneio' | 'txs';
28
+
29
+ type Resource = 'kv' | 'd1' | 'queue' | 'r2' | 'worker' | 'service' | 'orchestrator';
30
+
31
+ type Environment = number | 'dev' | 'test' | 'staging' | 'production';
32
+
33
+ interface SstInstance {
34
+ cloudflare: {
35
+ [key: string]: any;
36
+ };
37
+ }
38
+ declare class Infrastructure {
39
+ private project;
40
+ private environment;
41
+ private sst;
42
+ constructor({ project, environment, sst, }: {
43
+ project: Project;
44
+ environment: Environment;
45
+ sst: SstInstance;
46
+ });
47
+ composeBindingName({ resource, resourceName, bindingName, }: {
48
+ resource: Resource;
49
+ resourceName: string;
50
+ bindingName?: string;
51
+ }): string;
52
+ composeResourceName({ resourceName }: {
53
+ resourceName: string;
54
+ }): string;
55
+ composeKvArguments({ resourceName }: {
56
+ resourceName: string;
57
+ }): {
58
+ transform: {
59
+ namespace: {
60
+ title: string;
61
+ };
62
+ };
63
+ };
64
+ composeD1Arguments({ resourceName }: {
65
+ resourceName: string;
66
+ }): {
67
+ transform: {
68
+ database: {
69
+ name: string;
70
+ primaryLocationHint: string;
71
+ };
72
+ };
73
+ };
74
+ composeQueueArguments({ resourceName, deliveryDelay, messageRetentionPeriod, }: {
75
+ resourceName: string;
76
+ deliveryDelay?: number;
77
+ messageRetentionPeriod?: number;
78
+ }): {
79
+ transform: {
80
+ queue: {
81
+ queueName: string;
82
+ settings: {
83
+ deliveryDelay: number;
84
+ messageRetentionPeriod: number;
85
+ };
86
+ };
87
+ };
88
+ };
89
+ composeR2Arguments({ resourceName, storageClass, }: {
90
+ resourceName: string;
91
+ storageClass?: 'Standard' | 'InfrequentAccess';
92
+ }): {
93
+ transform: {
94
+ bucket: {
95
+ name: string;
96
+ jurisdiction: string;
97
+ location: string;
98
+ storageClass: "Standard" | "InfrequentAccess";
99
+ };
100
+ };
101
+ };
102
+ /**
103
+ * Creates an instance of Cloudflare KV.
104
+ */
105
+ kv(options: {
106
+ /**
107
+ * Name of the KV. Do not include the 'kv' prefix.
108
+ */
109
+ resourceName: string;
110
+ /**
111
+ * Name of the KV's binding. If not set, it is generated automatically based on the provided name.
112
+ */
113
+ bindingName?: string;
114
+ }): any;
115
+ /**
116
+ * Creates an instance of Cloudflare D1.
117
+ */
118
+ d1(options: {
119
+ /**
120
+ * Name of the D1. Do not include the 'd1' prefix.
121
+ */
122
+ resourceName: string;
123
+ /**
124
+ * Name of the D1's binding. If not set, it is generated automatically based on the provided name.
125
+ */
126
+ bindingName?: string;
127
+ }): any;
128
+ /**
129
+ * Creates an instance of Cloudflare Queue.
130
+ */
131
+ queue(options: {
132
+ /**
133
+ * Name of the Queue. Do not include the 'queue' prefix.
134
+ */
135
+ resourceName: string;
136
+ /**
137
+ * Name of the Queue's binding. If not set, it is generated automatically based on the provided name.
138
+ */
139
+ bindingName?: string;
140
+ /**
141
+ * The number of seconds to delay delivery of messages to the queue.
142
+ */
143
+ deliveryDelay?: number;
144
+ /**
145
+ * The number of seconds a message will remain in the queue before being deleted.
146
+ */
147
+ messageRetentionPeriod?: number;
148
+ }): any;
149
+ /**
150
+ * Creates an instance of Cloudflare R2.
151
+ */
152
+ r2(options: {
153
+ /**
154
+ * Name of the R2. Do not include the 'r2' prefix.
155
+ */
156
+ resourceName: string;
157
+ /**
158
+ * Name of the R2's binding. If not set, it is generated automatically based on the provided name.
159
+ */
160
+ bindingName?: string;
161
+ /**
162
+ * The storage class for the R2 bucket.
163
+ */
164
+ storageClass?: 'Standard' | 'InfrequentAccess';
165
+ }): any;
166
+ }
167
+
27
168
  type InternalErrorResponseStatus = Exclude<StatusCodes, 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207>;
28
169
  interface InternalError {
29
170
  status: InternalErrorResponseStatus;
@@ -406,5 +547,5 @@ interface WithRetryCounterOptions {
406
547
  type AsyncMethod<TArgs extends unknown[] = unknown[], TResult = unknown> = (...args: TArgs) => Promise<TResult>;
407
548
  declare function cloudflareQueue<TArgs extends unknown[] = unknown[], TResult = unknown>(options: WithRetryCounterOptions): (target: unknown, propertyKey: string | symbol, descriptor: TypedPropertyDescriptor<AsyncMethod<TArgs, TResult>>) => void;
408
549
 
409
- export { DatabaseTransaction, RPCResponse, action, base, basePostgres, calculateExponentialBackoff, cloudflareQueue, createAuditLogWriter, createInternalError, defineCommand, derivePortFromId, develitWorker, durableObjectNamespaceIdFromName, first, firstOrError, getD1Credentials, getD1DatabaseIdFromWrangler, getDrizzleD1Config, getDrizzlePgConfig, getPgCredentials, getPgDatabaseIdFromWrangler, getPgLocalConnectionString, handleAction, handleActionResponse, ibanZodSchema, isInternalError, paginationQuerySchema, paginationSchema, service, swiftZodSchema, useResult, useResultSync, uuidv4 };
410
- export type { ActionExecution, ActionHandlerOptions, AuditLogWriter, Command, CommandLogPayload, DevelitWorkerMethods, GatewayResponse, IRPCResponse, IncludeRelation, InferResultType, InternalError, InternalErrorResponseStatus, ValidatedInput };
550
+ export { DatabaseTransaction, Infrastructure, RPCResponse, action, base, basePostgres, calculateExponentialBackoff, cloudflareQueue, createAuditLogWriter, createInternalError, defineCommand, derivePortFromId, develitWorker, durableObjectNamespaceIdFromName, first, firstOrError, getD1Credentials, getD1DatabaseIdFromWrangler, getDrizzleD1Config, getDrizzlePgConfig, getPgCredentials, getPgDatabaseIdFromWrangler, getPgLocalConnectionString, handleAction, handleActionResponse, ibanZodSchema, isInternalError, paginationQuerySchema, paginationSchema, service, swiftZodSchema, useResult, useResultSync, uuidv4 };
551
+ export type { ActionExecution, ActionHandlerOptions, AuditLogWriter, Command, CommandLogPayload, DevelitWorkerMethods, Environment, GatewayResponse, IRPCResponse, IncludeRelation, InferResultType, InternalError, InternalErrorResponseStatus, Project, ValidatedInput };
package/dist/index.d.ts CHANGED
@@ -24,6 +24,147 @@ declare const basePostgres: {
24
24
  deletedAt: drizzle_orm.HasDefault<drizzle_orm_pg_core.PgTimestampBuilderInitial<"deleted_at">>;
25
25
  };
26
26
 
27
+ type Project = 'creditio' | 'fp' | 'mdm' | 'moneio' | 'txs';
28
+
29
+ type Resource = 'kv' | 'd1' | 'queue' | 'r2' | 'worker' | 'service' | 'orchestrator';
30
+
31
+ type Environment = number | 'dev' | 'test' | 'staging' | 'production';
32
+
33
+ interface SstInstance {
34
+ cloudflare: {
35
+ [key: string]: any;
36
+ };
37
+ }
38
+ declare class Infrastructure {
39
+ private project;
40
+ private environment;
41
+ private sst;
42
+ constructor({ project, environment, sst, }: {
43
+ project: Project;
44
+ environment: Environment;
45
+ sst: SstInstance;
46
+ });
47
+ composeBindingName({ resource, resourceName, bindingName, }: {
48
+ resource: Resource;
49
+ resourceName: string;
50
+ bindingName?: string;
51
+ }): string;
52
+ composeResourceName({ resourceName }: {
53
+ resourceName: string;
54
+ }): string;
55
+ composeKvArguments({ resourceName }: {
56
+ resourceName: string;
57
+ }): {
58
+ transform: {
59
+ namespace: {
60
+ title: string;
61
+ };
62
+ };
63
+ };
64
+ composeD1Arguments({ resourceName }: {
65
+ resourceName: string;
66
+ }): {
67
+ transform: {
68
+ database: {
69
+ name: string;
70
+ primaryLocationHint: string;
71
+ };
72
+ };
73
+ };
74
+ composeQueueArguments({ resourceName, deliveryDelay, messageRetentionPeriod, }: {
75
+ resourceName: string;
76
+ deliveryDelay?: number;
77
+ messageRetentionPeriod?: number;
78
+ }): {
79
+ transform: {
80
+ queue: {
81
+ queueName: string;
82
+ settings: {
83
+ deliveryDelay: number;
84
+ messageRetentionPeriod: number;
85
+ };
86
+ };
87
+ };
88
+ };
89
+ composeR2Arguments({ resourceName, storageClass, }: {
90
+ resourceName: string;
91
+ storageClass?: 'Standard' | 'InfrequentAccess';
92
+ }): {
93
+ transform: {
94
+ bucket: {
95
+ name: string;
96
+ jurisdiction: string;
97
+ location: string;
98
+ storageClass: "Standard" | "InfrequentAccess";
99
+ };
100
+ };
101
+ };
102
+ /**
103
+ * Creates an instance of Cloudflare KV.
104
+ */
105
+ kv(options: {
106
+ /**
107
+ * Name of the KV. Do not include the 'kv' prefix.
108
+ */
109
+ resourceName: string;
110
+ /**
111
+ * Name of the KV's binding. If not set, it is generated automatically based on the provided name.
112
+ */
113
+ bindingName?: string;
114
+ }): any;
115
+ /**
116
+ * Creates an instance of Cloudflare D1.
117
+ */
118
+ d1(options: {
119
+ /**
120
+ * Name of the D1. Do not include the 'd1' prefix.
121
+ */
122
+ resourceName: string;
123
+ /**
124
+ * Name of the D1's binding. If not set, it is generated automatically based on the provided name.
125
+ */
126
+ bindingName?: string;
127
+ }): any;
128
+ /**
129
+ * Creates an instance of Cloudflare Queue.
130
+ */
131
+ queue(options: {
132
+ /**
133
+ * Name of the Queue. Do not include the 'queue' prefix.
134
+ */
135
+ resourceName: string;
136
+ /**
137
+ * Name of the Queue's binding. If not set, it is generated automatically based on the provided name.
138
+ */
139
+ bindingName?: string;
140
+ /**
141
+ * The number of seconds to delay delivery of messages to the queue.
142
+ */
143
+ deliveryDelay?: number;
144
+ /**
145
+ * The number of seconds a message will remain in the queue before being deleted.
146
+ */
147
+ messageRetentionPeriod?: number;
148
+ }): any;
149
+ /**
150
+ * Creates an instance of Cloudflare R2.
151
+ */
152
+ r2(options: {
153
+ /**
154
+ * Name of the R2. Do not include the 'r2' prefix.
155
+ */
156
+ resourceName: string;
157
+ /**
158
+ * Name of the R2's binding. If not set, it is generated automatically based on the provided name.
159
+ */
160
+ bindingName?: string;
161
+ /**
162
+ * The storage class for the R2 bucket.
163
+ */
164
+ storageClass?: 'Standard' | 'InfrequentAccess';
165
+ }): any;
166
+ }
167
+
27
168
  type InternalErrorResponseStatus = Exclude<StatusCodes, 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207>;
28
169
  interface InternalError {
29
170
  status: InternalErrorResponseStatus;
@@ -406,5 +547,5 @@ interface WithRetryCounterOptions {
406
547
  type AsyncMethod<TArgs extends unknown[] = unknown[], TResult = unknown> = (...args: TArgs) => Promise<TResult>;
407
548
  declare function cloudflareQueue<TArgs extends unknown[] = unknown[], TResult = unknown>(options: WithRetryCounterOptions): (target: unknown, propertyKey: string | symbol, descriptor: TypedPropertyDescriptor<AsyncMethod<TArgs, TResult>>) => void;
408
549
 
409
- export { DatabaseTransaction, RPCResponse, action, base, basePostgres, calculateExponentialBackoff, cloudflareQueue, createAuditLogWriter, createInternalError, defineCommand, derivePortFromId, develitWorker, durableObjectNamespaceIdFromName, first, firstOrError, getD1Credentials, getD1DatabaseIdFromWrangler, getDrizzleD1Config, getDrizzlePgConfig, getPgCredentials, getPgDatabaseIdFromWrangler, getPgLocalConnectionString, handleAction, handleActionResponse, ibanZodSchema, isInternalError, paginationQuerySchema, paginationSchema, service, swiftZodSchema, useResult, useResultSync, uuidv4 };
410
- export type { ActionExecution, ActionHandlerOptions, AuditLogWriter, Command, CommandLogPayload, DevelitWorkerMethods, GatewayResponse, IRPCResponse, IncludeRelation, InferResultType, InternalError, InternalErrorResponseStatus, ValidatedInput };
550
+ export { DatabaseTransaction, Infrastructure, RPCResponse, action, base, basePostgres, calculateExponentialBackoff, cloudflareQueue, createAuditLogWriter, createInternalError, defineCommand, derivePortFromId, develitWorker, durableObjectNamespaceIdFromName, first, firstOrError, getD1Credentials, getD1DatabaseIdFromWrangler, getDrizzleD1Config, getDrizzlePgConfig, getPgCredentials, getPgDatabaseIdFromWrangler, getPgLocalConnectionString, handleAction, handleActionResponse, ibanZodSchema, isInternalError, paginationQuerySchema, paginationSchema, service, swiftZodSchema, useResult, useResultSync, uuidv4 };
551
+ export type { ActionExecution, ActionHandlerOptions, AuditLogWriter, Command, CommandLogPayload, DevelitWorkerMethods, Environment, GatewayResponse, IRPCResponse, IncludeRelation, InferResultType, InternalError, InternalErrorResponseStatus, Project, ValidatedInput };
package/dist/index.mjs CHANGED
@@ -1,6 +1,7 @@
1
1
  import { sql } from 'drizzle-orm';
2
2
  import { timestamp, uuid } from 'drizzle-orm/pg-core';
3
3
  import { integer, text } from 'drizzle-orm/sqlite-core';
4
+ import { toSnakeCase } from '@std/text';
4
5
  import 'http-status-codes';
5
6
  import * as z from 'zod/v4/core';
6
7
  import { createError } from 'h3';
@@ -29,6 +30,277 @@ const basePostgres = {
29
30
  }).default(sql`null`)
30
31
  };
31
32
 
33
+ class Infrastructure {
34
+ project;
35
+ environment;
36
+ sst;
37
+ constructor({
38
+ project,
39
+ environment,
40
+ sst
41
+ }) {
42
+ this.project = project;
43
+ this.environment = environment;
44
+ this.sst = sst;
45
+ }
46
+ // TODO(Pookensivee): Make tests for this util
47
+ composeBindingName({
48
+ resource,
49
+ resourceName,
50
+ bindingName
51
+ }) {
52
+ const convertedBindingName = bindingName ? toSnakeCase(bindingName) : `${toSnakeCase(resourceName)}_${resource}`;
53
+ return convertedBindingName.toUpperCase();
54
+ }
55
+ // TODO(Pookensivee): Make tests for this util
56
+ composeResourceName({ resourceName }) {
57
+ return `${this.project}-${resourceName}-${this.environment}`;
58
+ }
59
+ // TODO(Pookensivee): Make tests for this util
60
+ composeKvArguments({ resourceName }) {
61
+ return {
62
+ transform: {
63
+ namespace: {
64
+ title: resourceName
65
+ }
66
+ }
67
+ };
68
+ }
69
+ // TODO(Pookensivee): Make tests for this util
70
+ composeD1Arguments({ resourceName }) {
71
+ return {
72
+ transform: {
73
+ database: {
74
+ name: resourceName,
75
+ primaryLocationHint: "weur"
76
+ }
77
+ }
78
+ };
79
+ }
80
+ // TODO(Pookensivee): Make tests for this util
81
+ composeQueueArguments({
82
+ resourceName,
83
+ deliveryDelay = 5,
84
+ messageRetentionPeriod = 259200
85
+ }) {
86
+ return {
87
+ transform: {
88
+ queue: {
89
+ queueName: resourceName,
90
+ settings: {
91
+ deliveryDelay,
92
+ messageRetentionPeriod
93
+ }
94
+ }
95
+ }
96
+ };
97
+ }
98
+ // TODO(Pookensivee): Make tests for this util
99
+ composeR2Arguments({
100
+ resourceName,
101
+ storageClass = "Standard"
102
+ }) {
103
+ return {
104
+ transform: {
105
+ bucket: {
106
+ name: resourceName,
107
+ jurisdiction: "eu",
108
+ location: "weur",
109
+ storageClass
110
+ }
111
+ }
112
+ };
113
+ }
114
+ // TODO: Solve the circular dependency on post infrastructure deploy script
115
+ // TODO: Cannot assign a queue as a producer, work around: https://developers.cloudflare.com/workers/wrangler/commands/#queues-consumer-add-script-name
116
+ // async composeWorkerArguments({
117
+ // resourceName,
118
+ // path,
119
+ // bindings = [],
120
+ // }: {
121
+ // resourceName: string
122
+ // path: string
123
+ // bindings?: Input<Kv | D1 | Queue | Worker | Bucket>[]
124
+ // }) {
125
+ // const workerConfig = this.loadWorkerConfig({ path })
126
+ // const environmentVariables = this.extractEnvironmentVariables({ workerConfigEnv:
127
+ // workerConfig.env as WorkerConfigEnv
128
+ // })
129
+ // // TODO: Fix this
130
+ // if (
131
+ // 'SERVICE_CONFIG' in environmentVariables &&
132
+ // typeof environmentVariables.SERVICE_CONFIG === 'object'
133
+ // ) {
134
+ // environmentVariables.SERVICE_CONFIG = JSON.stringify(environmentVariables.SERVICE_CONFIG)
135
+ // }
136
+ // // TODO: Fix this
137
+ // if (
138
+ // 'EMAIL_SENDER' in environmentVariables &&
139
+ // typeof environmentVariables.EMAIL_SENDER === 'object'
140
+ // ) {
141
+ // environmentVariables.EMAIL_SENDER = JSON.stringify(environmentVariables.EMAIL_SENDER)
142
+ // }
143
+ // return {
144
+ // handler: join(path, './src/index.ts'),
145
+ // environment: Object.entries(environmentVariables).reduce((acc, [key, value]) => ({
146
+ // ...acc,
147
+ // [key]: String(value)
148
+ // }), {} as Record<string, string>),
149
+ // link: bindings,
150
+ // transform: {
151
+ // worker: {
152
+ // scriptName: this.composeResourceName({ resourceName }),
153
+ // compatibilityDate: '2025-06-04',
154
+ // compatibilityFlags: ['nodejs_compat'],
155
+ // observability: {
156
+ // enabled: true,
157
+ // headSamplingRate: 1,
158
+ // logs: {
159
+ // // Check whether this disables logs completely or just invocation ones
160
+ // enabled: false,
161
+ // invocationLogs: false,
162
+ // },
163
+ // },
164
+ // },
165
+ // }
166
+ // } satisfies Partial<WorkerArgs>
167
+ // }
168
+ // loadWorkerConfig({ path }: { path: string }) {
169
+ // const workerConfigFile = readFileSync(
170
+ // join(path, './wrangler.jsonc'),
171
+ // 'utf-8',
172
+ // )
173
+ // TODO: Use parse from comment-json
174
+ // const jsonString = workerConfigFile
175
+ // .replace(/\/\*[\s\S]*?\*\//g, '')
176
+ // .replace(/\/\/.*$/gm, '')
177
+ // return JSON.parse(jsonString)
178
+ // }
179
+ // extractEnvironmentVariables({
180
+ // workerConfigEnv,
181
+ // }: {
182
+ // workerConfigEnv: WorkerConfigEnv
183
+ // }) {
184
+ // if (typeof this.environment === 'number') {
185
+ // return { ...workerConfigEnv.dev.vars, ENVIRONMENT: this.environment }
186
+ // }
187
+ // return { ...workerConfigEnv[this.environment].vars }
188
+ // }
189
+ /**
190
+ * Creates an instance of Cloudflare KV.
191
+ */
192
+ kv(options) {
193
+ const { resourceName, bindingName } = options;
194
+ return new this.sst.cloudflare.Kv(
195
+ `${this.composeBindingName({ resource: "kv", resourceName, bindingName })}`,
196
+ this.composeKvArguments({
197
+ resourceName: this.composeResourceName({ resourceName })
198
+ })
199
+ );
200
+ }
201
+ /**
202
+ * Creates an instance of Cloudflare D1.
203
+ */
204
+ d1(options) {
205
+ const { resourceName, bindingName } = options;
206
+ return new this.sst.cloudflare.D1(
207
+ `${this.composeBindingName({ resource: "d1", resourceName, bindingName })}`,
208
+ this.composeD1Arguments({
209
+ resourceName: this.composeResourceName({ resourceName })
210
+ })
211
+ );
212
+ }
213
+ /**
214
+ * Creates an instance of Cloudflare Queue.
215
+ */
216
+ queue(options) {
217
+ const { resourceName, bindingName, deliveryDelay, messageRetentionPeriod } = options;
218
+ return new this.sst.cloudflare.Queue(
219
+ `${this.composeBindingName({ resource: "queue", resourceName, bindingName })}`,
220
+ this.composeQueueArguments({
221
+ resourceName: this.composeResourceName({ resourceName }),
222
+ deliveryDelay,
223
+ messageRetentionPeriod
224
+ })
225
+ );
226
+ }
227
+ /**
228
+ * Creates an instance of Cloudflare R2.
229
+ */
230
+ r2(options) {
231
+ const { resourceName, bindingName, storageClass } = options;
232
+ return new this.sst.cloudflare.Bucket(
233
+ `${this.composeBindingName({ resource: "r2", resourceName, bindingName })}`,
234
+ this.composeR2Arguments({
235
+ resourceName: this.composeResourceName({ resourceName }),
236
+ storageClass
237
+ })
238
+ );
239
+ }
240
+ // TODO: Solve the circular dependency on post infrastructure deploy script
241
+ // async worker({
242
+ // resourceName,
243
+ // bindingName,
244
+ // path,
245
+ // bindings = [],
246
+ // }: {
247
+ // resourceName: string
248
+ // bindingName: string
249
+ // path: string
250
+ // bindings?: Input<Kv | D1 | Queue | Worker | Bucket>[]
251
+ // }) {
252
+ // return new sst.cloudflare.Worker(
253
+ // this.composeBindingName({
254
+ // resource: 'worker',
255
+ // bindingName,
256
+ // resourceName,
257
+ // }),
258
+ // await this.composeWorkerArguments({
259
+ // resourceName: this.composeResourceName({ resourceName }),
260
+ // path,
261
+ // bindings,
262
+ // }),
263
+ // )
264
+ // }
265
+ // async service({
266
+ // resourceName,
267
+ // bindingName,
268
+ // path,
269
+ // bindings = [],
270
+ // }: {
271
+ // resourceName: string
272
+ // bindingName?: string
273
+ // path?: string
274
+ // bindings?: Input<Kv | D1 | Queue | Worker | Bucket>[]
275
+ // }) {
276
+ // return this.worker({
277
+ // resourceName: `${this.project}-${resourceName}-service-${this.environment}`,
278
+ // bindingName: this.composeBindingName({
279
+ // resource: 'service',
280
+ // bindingName,
281
+ // resourceName,
282
+ // }),
283
+ // path: `${path ?? `./services/${resourceName}`}`,
284
+ // bindings,
285
+ // })
286
+ // }
287
+ // // TODO: Add name
288
+ // async orchestrator({
289
+ // path,
290
+ // bindings = [],
291
+ // }: {
292
+ // path?: string
293
+ // bindings?: Input<Kv | D1 | Queue | Worker | Bucket>[]
294
+ // }) {
295
+ // return this.worker({
296
+ // resourceName: `${this.project}-gateway-${this.environment}`,
297
+ // bindingName: 'GATEWAY',
298
+ // path: `${path ?? `./apps/gateway`}`,
299
+ // bindings,
300
+ // })
301
+ // }
302
+ }
303
+
32
304
  const ibanZodSchema = new z.$ZodString({
33
305
  type: "string",
34
306
  checks: [
@@ -656,4 +928,4 @@ function develitWorker(Worker) {
656
928
  return DevelitWorker;
657
929
  }
658
930
 
659
- export { DatabaseTransaction, RPCResponse, action, base, basePostgres, calculateExponentialBackoff, cloudflareQueue, createAuditLogWriter, createInternalError, defineCommand, derivePortFromId, develitWorker, durableObjectNamespaceIdFromName, first, firstOrError, getD1Credentials, getD1DatabaseIdFromWrangler, getDrizzleD1Config, getDrizzlePgConfig, getPgCredentials, getPgDatabaseIdFromWrangler, getPgLocalConnectionString, handleAction, handleActionResponse, ibanZodSchema, isInternalError, paginationQuerySchema, paginationSchema, service, swiftZodSchema, useResult, useResultSync, uuidv4 };
931
+ export { DatabaseTransaction, Infrastructure, RPCResponse, action, base, basePostgres, calculateExponentialBackoff, cloudflareQueue, createAuditLogWriter, createInternalError, defineCommand, derivePortFromId, develitWorker, durableObjectNamespaceIdFromName, first, firstOrError, getD1Credentials, getD1DatabaseIdFromWrangler, getDrizzleD1Config, getDrizzlePgConfig, getPgCredentials, getPgDatabaseIdFromWrangler, getPgLocalConnectionString, handleAction, handleActionResponse, ibanZodSchema, isInternalError, paginationQuerySchema, paginationSchema, service, swiftZodSchema, useResult, useResultSync, uuidv4 };
package/package.json CHANGED
@@ -1,20 +1,20 @@
1
1
  {
2
2
  "name": "@develit-io/backend-sdk",
3
- "version": "5.13.0",
3
+ "version": "5.14.1",
4
4
  "description": "Develit Backend SDK",
5
5
  "author": "Develit.io",
6
6
  "license": "ISC",
7
7
  "type": "module",
8
8
  "scripts": {
9
- "prepack": "unbuild",
10
- "typecheck": "tsc",
9
+ "build": "unbuild",
10
+ "changelogen": "bunx changelogen@latest --bump",
11
11
  "lint": "biome check",
12
12
  "lint:fix": "biome check --fix",
13
- "build": "unbuild",
13
+ "prepack": "unbuild",
14
+ "release": "bun run build && bunx changelogen@latest --release --push && npm publish --access public",
14
15
  "test": "vitest",
15
16
  "test:unit": "vitest test/unit",
16
- "changelogen": "bunx changelogen@latest --bump",
17
- "release": "bun run build && bunx changelogen@latest --release --push && npm publish --access public"
17
+ "typecheck": "tsc"
18
18
  },
19
19
  "exports": {
20
20
  ".": {
@@ -26,18 +26,24 @@
26
26
  },
27
27
  "main": "./dist/index.cjs",
28
28
  "types": "./dist/index.d.ts",
29
- "files": ["dist"],
29
+ "files": [
30
+ "dist"
31
+ ],
30
32
  "dependencies": {
31
- "@cloudflare/workers-types": "^4.20250823.0",
33
+ "@cloudflare/workers-types": "4.20250909.0",
34
+ "@pulumi/cloudflare": "^6.8.0",
35
+ "@std/path": "npm:@jsr/std__path",
36
+ "@std/text": "npm:@jsr/std__text",
32
37
  "comment-json": "^4.2.5",
33
38
  "consola": "^3.4.2",
34
39
  "drizzle-kit": "^0.31.4",
35
- "drizzle-orm": "^0.44.4",
40
+ "drizzle-orm": "^0.44.5",
36
41
  "h3": "^1.15.4",
37
42
  "http-status-codes": "2.3.0",
38
43
  "superjson": "^2.2.2"
39
44
  },
40
45
  "peerDependencies": {
41
- "zod": "^4.1.1"
46
+ "sst": "^3.17.12",
47
+ "zod": "^4.1.5"
42
48
  }
43
49
  }