@stacksjs/ts-cloud 0.7.5 → 0.7.7

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.
Files changed (57) hide show
  1. package/dist/aws/index.js +259 -0
  2. package/dist/bin/cli.js +231 -231
  3. package/dist/chunk-0wxyppza.js +146 -0
  4. package/dist/chunk-3knnr7wh.js +601 -0
  5. package/dist/chunk-3rsfns7x.js +10427 -0
  6. package/dist/chunk-93hjhs78.js +1752 -0
  7. package/dist/chunk-arsh1g5h.js +1749 -0
  8. package/dist/chunk-b82pbxyp.js +1572 -0
  9. package/dist/chunk-c6rgvg1j.js +46124 -0
  10. package/dist/chunk-d2p5n2aq.js +23 -0
  11. package/dist/chunk-d7p84vz5.js +1699 -0
  12. package/dist/chunk-eq08r166.js +8 -0
  13. package/dist/chunk-hsk6fe6x.js +371 -0
  14. package/dist/chunk-mj1tmhte.js +13 -0
  15. package/dist/chunk-pegd7rmf.js +10 -0
  16. package/dist/chunk-pqfzdg68.js +12 -0
  17. package/dist/chunk-qpj3edwz.js +601 -0
  18. package/dist/chunk-tjjgajbh.js +1032 -0
  19. package/dist/chunk-tnztxpcb.js +630 -0
  20. package/dist/chunk-tt4kxske.js +1788 -0
  21. package/dist/chunk-v0bahtg2.js +4 -0
  22. package/dist/chunk-vd87cpvn.js +1754 -0
  23. package/dist/chunk-x07dz3sd.js +12828 -0
  24. package/dist/chunk-xzn0ntr0.js +4616 -0
  25. package/dist/chunk-zn0nxxa8.js +1779 -0
  26. package/dist/deploy/index.js +87 -0
  27. package/dist/dns/index.js +31 -0
  28. package/dist/drivers/index.d.ts +2 -0
  29. package/dist/drivers/index.js +108 -0
  30. package/dist/drivers/shared/box-provision.d.ts +102 -0
  31. package/dist/index.d.ts +1 -1
  32. package/dist/index.js +4383 -91358
  33. package/dist/push/index.js +509 -0
  34. package/dist/ui/index.html +3 -3
  35. package/dist/ui/server/actions.html +3 -3
  36. package/dist/ui/server/backups.html +3 -3
  37. package/dist/ui/server/database.html +3 -3
  38. package/dist/ui/server/deployments.html +3 -3
  39. package/dist/ui/server/firewall.html +3 -3
  40. package/dist/ui/server/logs.html +3 -3
  41. package/dist/ui/server/services.html +3 -3
  42. package/dist/ui/server/sites.html +3 -3
  43. package/dist/ui/server/ssh-keys.html +3 -3
  44. package/dist/ui/server/terminal.html +3 -3
  45. package/dist/ui/server/workers.html +3 -3
  46. package/dist/ui/serverless/alarms.html +3 -3
  47. package/dist/ui/serverless/assets.html +3 -3
  48. package/dist/ui/serverless/data.html +3 -3
  49. package/dist/ui/serverless/deployments.html +3 -3
  50. package/dist/ui/serverless/functions.html +3 -3
  51. package/dist/ui/serverless/logs.html +3 -3
  52. package/dist/ui/serverless/queues.html +3 -3
  53. package/dist/ui/serverless/scheduler.html +3 -3
  54. package/dist/ui/serverless/secrets.html +3 -3
  55. package/dist/ui/serverless/traces.html +3 -3
  56. package/dist/ui/serverless.html +3 -3
  57. package/package.json +3 -3
@@ -0,0 +1,509 @@
1
+ import"../chunk-v0bahtg2.js";
2
+
3
+ // src/push/apns.ts
4
+ import { createSign } from "node:crypto";
5
+ import * as http2 from "node:http2";
6
+ var APNS_PRODUCTION_HOST = "api.push.apple.com";
7
+ var APNS_SANDBOX_HOST = "api.sandbox.push.apple.com";
8
+ var TOKEN_EXPIRY_MS = 45 * 60 * 1000;
9
+
10
+ class APNsClient {
11
+ config;
12
+ token = null;
13
+ tokenGeneratedAt = 0;
14
+ client = null;
15
+ host;
16
+ constructor(config) {
17
+ this.config = config;
18
+ this.host = config.production ? APNS_PRODUCTION_HOST : APNS_SANDBOX_HOST;
19
+ }
20
+ generateToken() {
21
+ const now = Math.floor(Date.now() / 1000);
22
+ if (this.token && Date.now() - this.tokenGeneratedAt < TOKEN_EXPIRY_MS) {
23
+ return this.token;
24
+ }
25
+ const header = {
26
+ alg: "ES256",
27
+ kid: this.config.keyId
28
+ };
29
+ const payload = {
30
+ iss: this.config.teamId,
31
+ iat: now
32
+ };
33
+ const encodedHeader = Buffer.from(JSON.stringify(header)).toString("base64url");
34
+ const encodedPayload = Buffer.from(JSON.stringify(payload)).toString("base64url");
35
+ const signatureInput = `${encodedHeader}.${encodedPayload}`;
36
+ const sign = createSign("SHA256");
37
+ sign.update(signatureInput);
38
+ const signature = sign.sign(this.config.privateKey);
39
+ const rawSignature = this.derToRaw(signature);
40
+ const encodedSignature = rawSignature.toString("base64url");
41
+ this.token = `${signatureInput}.${encodedSignature}`;
42
+ this.tokenGeneratedAt = Date.now();
43
+ return this.token;
44
+ }
45
+ derToRaw(derSignature) {
46
+ let offset = 2;
47
+ if (derSignature[offset] !== 2) {
48
+ throw new Error("Invalid DER signature: expected 0x02 for R");
49
+ }
50
+ offset++;
51
+ const rLength = derSignature[offset];
52
+ offset++;
53
+ let r = derSignature.subarray(offset, offset + rLength);
54
+ offset += rLength;
55
+ if (derSignature[offset] !== 2) {
56
+ throw new Error("Invalid DER signature: expected 0x02 for S");
57
+ }
58
+ offset++;
59
+ const sLength = derSignature[offset];
60
+ offset++;
61
+ let s = derSignature.subarray(offset, offset + sLength);
62
+ if (r[0] === 0 && r.length === 33) {
63
+ r = r.subarray(1);
64
+ }
65
+ if (s[0] === 0 && s.length === 33) {
66
+ s = s.subarray(1);
67
+ }
68
+ const result = Buffer.alloc(64);
69
+ r.copy(result, 32 - r.length);
70
+ s.copy(result, 64 - s.length);
71
+ return result;
72
+ }
73
+ async getClient() {
74
+ if (this.client && !this.client.destroyed) {
75
+ return this.client;
76
+ }
77
+ return new Promise((resolve, reject) => {
78
+ this.client = http2.connect(`https://${this.host}`);
79
+ this.client.on("error", (err) => {
80
+ reject(err);
81
+ });
82
+ this.client.on("connect", () => {
83
+ resolve(this.client);
84
+ });
85
+ this.client.on("close", () => {
86
+ this.client = null;
87
+ });
88
+ });
89
+ }
90
+ buildPayload(notification) {
91
+ const aps = {};
92
+ if (notification.title || notification.body || notification.subtitle) {
93
+ aps.alert = {};
94
+ if (notification.title)
95
+ aps.alert.title = notification.title;
96
+ if (notification.subtitle)
97
+ aps.alert.subtitle = notification.subtitle;
98
+ if (notification.body)
99
+ aps.alert.body = notification.body;
100
+ }
101
+ if (notification.badge !== undefined) {
102
+ aps.badge = notification.badge;
103
+ }
104
+ if (notification.sound !== undefined) {
105
+ aps.sound = notification.sound;
106
+ }
107
+ if (notification.category) {
108
+ aps.category = notification.category;
109
+ }
110
+ if (notification.threadId) {
111
+ aps["thread-id"] = notification.threadId;
112
+ }
113
+ if (notification.contentAvailable) {
114
+ aps["content-available"] = 1;
115
+ }
116
+ if (notification.mutableContent) {
117
+ aps["mutable-content"] = 1;
118
+ }
119
+ if (notification.targetContentId) {
120
+ aps["target-content-id"] = notification.targetContentId;
121
+ }
122
+ const payload = { aps };
123
+ if (notification.data) {
124
+ Object.assign(payload, notification.data);
125
+ }
126
+ return payload;
127
+ }
128
+ async send(notification) {
129
+ try {
130
+ const client = await this.getClient();
131
+ const token = this.generateToken();
132
+ const payload = JSON.stringify(this.buildPayload(notification));
133
+ const headers = {
134
+ ":method": "POST",
135
+ ":path": `/3/device/${notification.deviceToken}`,
136
+ authorization: `bearer ${token}`,
137
+ "apns-topic": this.config.bundleId,
138
+ "apns-push-type": notification.pushType || "alert",
139
+ "apns-priority": String(notification.priority || 10)
140
+ };
141
+ if (notification.expiration !== undefined) {
142
+ headers["apns-expiration"] = String(notification.expiration);
143
+ }
144
+ if (notification.collapseId) {
145
+ headers["apns-collapse-id"] = notification.collapseId;
146
+ }
147
+ return new Promise((resolve) => {
148
+ const req = client.request(headers);
149
+ let responseData = "";
150
+ let statusCode = 0;
151
+ let apnsId;
152
+ req.on("response", (headers2) => {
153
+ statusCode = headers2[":status"];
154
+ apnsId = headers2["apns-id"];
155
+ });
156
+ req.on("data", (chunk) => {
157
+ responseData += chunk.toString();
158
+ });
159
+ req.on("end", () => {
160
+ if (statusCode === 200) {
161
+ resolve({
162
+ success: true,
163
+ deviceToken: notification.deviceToken,
164
+ apnsId,
165
+ statusCode
166
+ });
167
+ } else {
168
+ let error = "Unknown error";
169
+ let reason;
170
+ let timestamp;
171
+ if (responseData) {
172
+ try {
173
+ const parsed = JSON.parse(responseData);
174
+ reason = parsed.reason;
175
+ timestamp = parsed.timestamp;
176
+ error = reason || error;
177
+ } catch {
178
+ error = responseData;
179
+ }
180
+ }
181
+ resolve({
182
+ success: false,
183
+ deviceToken: notification.deviceToken,
184
+ apnsId,
185
+ statusCode,
186
+ error,
187
+ reason,
188
+ timestamp
189
+ });
190
+ }
191
+ });
192
+ req.on("error", (err) => {
193
+ resolve({
194
+ success: false,
195
+ deviceToken: notification.deviceToken,
196
+ error: err.message
197
+ });
198
+ });
199
+ req.write(payload);
200
+ req.end();
201
+ });
202
+ } catch (error) {
203
+ return {
204
+ success: false,
205
+ deviceToken: notification.deviceToken,
206
+ error: error.message
207
+ };
208
+ }
209
+ }
210
+ async sendBatch(notifications, options) {
211
+ const concurrency = options?.concurrency || 10;
212
+ const results = [];
213
+ for (let i = 0;i < notifications.length; i += concurrency) {
214
+ const batch = notifications.slice(i, i + concurrency);
215
+ const batchResults = await Promise.all(batch.map((n) => this.send(n)));
216
+ results.push(...batchResults);
217
+ }
218
+ return {
219
+ sent: results.filter((r) => r.success).length,
220
+ failed: results.filter((r) => !r.success).length,
221
+ results
222
+ };
223
+ }
224
+ async sendSimple(deviceToken, title, body, data) {
225
+ return this.send({
226
+ deviceToken,
227
+ title,
228
+ body,
229
+ data
230
+ });
231
+ }
232
+ async sendSilent(deviceToken, data) {
233
+ return this.send({
234
+ deviceToken,
235
+ contentAvailable: true,
236
+ pushType: "background",
237
+ priority: 5,
238
+ data
239
+ });
240
+ }
241
+ close() {
242
+ if (this.client) {
243
+ this.client.close();
244
+ this.client = null;
245
+ }
246
+ this.token = null;
247
+ this.tokenGeneratedAt = 0;
248
+ }
249
+ }
250
+ // src/push/fcm.ts
251
+ import { createSign as createSign2 } from "node:crypto";
252
+ var FCM_API_URL = "https://fcm.googleapis.com/v1/projects";
253
+ var GOOGLE_TOKEN_URL = "https://oauth2.googleapis.com/token";
254
+ var TOKEN_EXPIRY_MS2 = 55 * 60 * 1000;
255
+
256
+ class FCMClient {
257
+ config;
258
+ accessToken = null;
259
+ tokenExpiresAt = 0;
260
+ constructor(config) {
261
+ this.config = config;
262
+ }
263
+ static fromServiceAccount(serviceAccount) {
264
+ return new FCMClient({
265
+ projectId: serviceAccount.project_id,
266
+ clientEmail: serviceAccount.client_email,
267
+ privateKey: serviceAccount.private_key
268
+ });
269
+ }
270
+ generateJWT() {
271
+ const now = Math.floor(Date.now() / 1000);
272
+ const exp = now + 3600;
273
+ const header = {
274
+ alg: "RS256",
275
+ typ: "JWT"
276
+ };
277
+ const payload = {
278
+ iss: this.config.clientEmail,
279
+ sub: this.config.clientEmail,
280
+ aud: GOOGLE_TOKEN_URL,
281
+ iat: now,
282
+ exp,
283
+ scope: "https://www.googleapis.com/auth/firebase.messaging"
284
+ };
285
+ const encodedHeader = Buffer.from(JSON.stringify(header)).toString("base64url");
286
+ const encodedPayload = Buffer.from(JSON.stringify(payload)).toString("base64url");
287
+ const signatureInput = `${encodedHeader}.${encodedPayload}`;
288
+ const sign = createSign2("SHA256");
289
+ sign.update(signatureInput);
290
+ const signature = sign.sign(this.config.privateKey, "base64url");
291
+ return `${signatureInput}.${signature}`;
292
+ }
293
+ async getAccessToken() {
294
+ if (this.accessToken && Date.now() < this.tokenExpiresAt) {
295
+ return this.accessToken;
296
+ }
297
+ const jwt = this.generateJWT();
298
+ const response = await fetch(GOOGLE_TOKEN_URL, {
299
+ method: "POST",
300
+ headers: {
301
+ "Content-Type": "application/x-www-form-urlencoded"
302
+ },
303
+ body: new URLSearchParams({
304
+ grant_type: "urn:ietf:params:oauth:grant-type:jwt-bearer",
305
+ assertion: jwt
306
+ }).toString()
307
+ });
308
+ if (!response.ok) {
309
+ const errorText = await response.text();
310
+ throw new Error(`Failed to get access token: ${errorText}`);
311
+ }
312
+ const data = await response.json();
313
+ this.accessToken = data.access_token;
314
+ this.tokenExpiresAt = Date.now() + TOKEN_EXPIRY_MS2;
315
+ return this.accessToken;
316
+ }
317
+ buildMessage(notification) {
318
+ const message = {};
319
+ if (notification.token) {
320
+ message.token = notification.token;
321
+ } else if (notification.topic) {
322
+ message.topic = notification.topic;
323
+ } else if (notification.condition) {
324
+ message.condition = notification.condition;
325
+ }
326
+ if (notification.title || notification.body || notification.imageUrl) {
327
+ message.notification = {};
328
+ if (notification.title)
329
+ message.notification.title = notification.title;
330
+ if (notification.body)
331
+ message.notification.body = notification.body;
332
+ if (notification.imageUrl)
333
+ message.notification.image = notification.imageUrl;
334
+ }
335
+ if (notification.data && Object.keys(notification.data).length > 0) {
336
+ message.data = notification.data;
337
+ }
338
+ if (notification.android) {
339
+ message.android = {
340
+ priority: notification.android.priority || "high"
341
+ };
342
+ if (notification.android.ttl) {
343
+ message.android.ttl = `${notification.android.ttl}s`;
344
+ }
345
+ if (notification.android.collapseKey) {
346
+ message.android.collapse_key = notification.android.collapseKey;
347
+ }
348
+ if (notification.android.directBootOk) {
349
+ message.android.direct_boot_ok = notification.android.directBootOk;
350
+ }
351
+ const androidNotification = {};
352
+ if (notification.android.channelId)
353
+ androidNotification.channel_id = notification.android.channelId;
354
+ if (notification.android.icon)
355
+ androidNotification.icon = notification.android.icon;
356
+ if (notification.android.color)
357
+ androidNotification.color = notification.android.color;
358
+ if (notification.android.sound)
359
+ androidNotification.sound = notification.android.sound;
360
+ if (notification.android.clickAction)
361
+ androidNotification.click_action = notification.android.clickAction;
362
+ if (notification.android.tag)
363
+ androidNotification.tag = notification.android.tag;
364
+ if (notification.android.visibility)
365
+ androidNotification.visibility = notification.android.visibility;
366
+ if (notification.android.notificationCount !== undefined) {
367
+ androidNotification.notification_count = notification.android.notificationCount;
368
+ }
369
+ if (Object.keys(androidNotification).length > 0) {
370
+ message.android.notification = androidNotification;
371
+ }
372
+ }
373
+ if (notification.webpush) {
374
+ message.webpush = notification.webpush;
375
+ }
376
+ if (notification.apns) {
377
+ message.apns = notification.apns;
378
+ }
379
+ if (notification.fcmOptions) {
380
+ message.fcm_options = notification.fcmOptions;
381
+ }
382
+ return { message };
383
+ }
384
+ async send(notification) {
385
+ try {
386
+ const accessToken = await this.getAccessToken();
387
+ const payload = this.buildMessage(notification);
388
+ const response = await fetch(`${FCM_API_URL}/${this.config.projectId}/messages:send`, {
389
+ method: "POST",
390
+ headers: {
391
+ Authorization: `Bearer ${accessToken}`,
392
+ "Content-Type": "application/json"
393
+ },
394
+ body: JSON.stringify(payload)
395
+ });
396
+ const data = await response.json();
397
+ if (response.ok) {
398
+ return {
399
+ success: true,
400
+ messageId: data.name
401
+ };
402
+ } else {
403
+ return {
404
+ success: false,
405
+ error: data.error?.message || "Unknown error",
406
+ errorCode: data.error?.status
407
+ };
408
+ }
409
+ } catch (error) {
410
+ return {
411
+ success: false,
412
+ error: error.message
413
+ };
414
+ }
415
+ }
416
+ async sendBatch(tokens, notification, options) {
417
+ const concurrency = options?.concurrency || 10;
418
+ const results = [];
419
+ for (let i = 0;i < tokens.length; i += concurrency) {
420
+ const batch = tokens.slice(i, i + concurrency);
421
+ const batchPromises = batch.map(async (token) => {
422
+ const result = await this.send({ ...notification, token });
423
+ return { ...result, token };
424
+ });
425
+ const batchResults = await Promise.all(batchPromises);
426
+ results.push(...batchResults);
427
+ }
428
+ return {
429
+ sent: results.filter((r) => r.success).length,
430
+ failed: results.filter((r) => !r.success).length,
431
+ results
432
+ };
433
+ }
434
+ async sendToTopic(topic, notification) {
435
+ return this.send({ ...notification, topic });
436
+ }
437
+ async sendToCondition(condition, notification) {
438
+ return this.send({ ...notification, condition });
439
+ }
440
+ async sendSimple(token, title, body, data) {
441
+ return this.send({
442
+ token,
443
+ title,
444
+ body,
445
+ data
446
+ });
447
+ }
448
+ async sendSilent(token, data) {
449
+ return this.send({
450
+ token,
451
+ data,
452
+ android: {
453
+ priority: "high"
454
+ }
455
+ });
456
+ }
457
+ async subscribeToTopic(tokens, topic) {
458
+ try {
459
+ const accessToken = await this.getAccessToken();
460
+ const response = await fetch(`https://iid.googleapis.com/iid/v1:batchAdd`, {
461
+ method: "POST",
462
+ headers: {
463
+ Authorization: `Bearer ${accessToken}`,
464
+ "Content-Type": "application/json"
465
+ },
466
+ body: JSON.stringify({
467
+ to: `/topics/${topic}`,
468
+ registration_tokens: tokens
469
+ })
470
+ });
471
+ if (response.ok) {
472
+ return { success: true };
473
+ } else {
474
+ const data = await response.json();
475
+ return { success: false, error: data.error?.message || "Failed to subscribe" };
476
+ }
477
+ } catch (error) {
478
+ return { success: false, error: error.message };
479
+ }
480
+ }
481
+ async unsubscribeFromTopic(tokens, topic) {
482
+ try {
483
+ const accessToken = await this.getAccessToken();
484
+ const response = await fetch(`https://iid.googleapis.com/iid/v1:batchRemove`, {
485
+ method: "POST",
486
+ headers: {
487
+ Authorization: `Bearer ${accessToken}`,
488
+ "Content-Type": "application/json"
489
+ },
490
+ body: JSON.stringify({
491
+ to: `/topics/${topic}`,
492
+ registration_tokens: tokens
493
+ })
494
+ });
495
+ if (response.ok) {
496
+ return { success: true };
497
+ } else {
498
+ const data = await response.json();
499
+ return { success: false, error: data.error?.message || "Failed to unsubscribe" };
500
+ }
501
+ } catch (error) {
502
+ return { success: false, error: error.message };
503
+ }
504
+ }
505
+ }
506
+ export {
507
+ FCMClient,
508
+ APNsClient
509
+ };