puls-dev 0.1.7 → 0.1.8

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 (45) hide show
  1. package/README.md +10 -8
  2. package/dist/core/checker.d.ts +1 -1
  3. package/dist/core/checker.js +88 -56
  4. package/dist/core/decorators.js +8 -2
  5. package/dist/core/resource.js +2 -2
  6. package/dist/core/stack.d.ts +1 -1
  7. package/dist/core/stack.js +2 -2
  8. package/dist/providers/aws/acm.d.ts +1 -1
  9. package/dist/providers/aws/acm.js +27 -23
  10. package/dist/providers/aws/api.d.ts +14 -14
  11. package/dist/providers/aws/api.js +21 -21
  12. package/dist/providers/aws/apigateway.d.ts +2 -2
  13. package/dist/providers/aws/apigateway.js +33 -29
  14. package/dist/providers/aws/cloudfront.d.ts +3 -3
  15. package/dist/providers/aws/cloudfront.js +49 -34
  16. package/dist/providers/aws/fargate.d.ts +2 -2
  17. package/dist/providers/aws/fargate.js +99 -52
  18. package/dist/providers/aws/lambda.d.ts +2 -2
  19. package/dist/providers/aws/lambda.js +63 -32
  20. package/dist/providers/aws/rds.d.ts +1 -1
  21. package/dist/providers/aws/rds.js +77 -39
  22. package/dist/providers/aws/route53.d.ts +5 -5
  23. package/dist/providers/aws/route53.js +42 -35
  24. package/dist/providers/aws/s3.d.ts +2 -2
  25. package/dist/providers/aws/s3.js +40 -33
  26. package/dist/providers/aws/secrets.js +15 -7
  27. package/dist/providers/aws/sqs.d.ts +1 -1
  28. package/dist/providers/aws/sqs.js +47 -23
  29. package/dist/providers/do/domain.d.ts +4 -4
  30. package/dist/providers/do/domain.js +15 -11
  31. package/dist/providers/firebase/auth.d.ts +1 -1
  32. package/dist/providers/firebase/auth.js +65 -33
  33. package/dist/providers/firebase/firestore.d.ts +2 -2
  34. package/dist/providers/firebase/firestore.js +45 -28
  35. package/dist/providers/firebase/functions.d.ts +1 -1
  36. package/dist/providers/firebase/functions.js +75 -42
  37. package/dist/providers/firebase/hosting.d.ts +1 -1
  38. package/dist/providers/firebase/hosting.js +92 -52
  39. package/dist/providers/firebase/remoteconfig.d.ts +1 -1
  40. package/dist/providers/firebase/remoteconfig.js +42 -33
  41. package/dist/providers/firebase/storage.d.ts +1 -1
  42. package/dist/providers/firebase/storage.js +38 -24
  43. package/dist/providers/proxmox/vm.js +34 -22
  44. package/dist/types/aws.js +1 -1
  45. package/package.json +2 -1
@@ -31,7 +31,7 @@ export class SecretsBuilder extends BaseBuilder {
31
31
  throw e;
32
32
  }
33
33
  }
34
- // Awaits eager discovery used by resolveEnvVars so callers don't need discoveryPromise directly
34
+ // Awaits eager discovery - used by resolveEnvVars so callers don't need discoveryPromise directly
35
35
  async awaitValue() {
36
36
  await this.discoveryPromise;
37
37
  return this.resolvedValue;
@@ -70,11 +70,15 @@ export class SecretsBuilder extends BaseBuilder {
70
70
  if (this._description)
71
71
  console.log(` └─ Description: ${this._description}`);
72
72
  }
73
- return { name: this.name, arn: this.resolvedArn, value: this.resolvedValue };
73
+ return {
74
+ name: this.name,
75
+ arn: this.resolvedArn,
76
+ value: this.resolvedValue,
77
+ };
74
78
  }
75
79
  if (!existing) {
76
80
  if (!this._value) {
77
- console.log(` ⚠️ Secret "${this.name}" does not exist add .plainText() or .keyValue() to create it`);
81
+ console.log(` ⚠️ Secret "${this.name}" does not exist - add .plainText() or .keyValue() to create it`);
78
82
  return { name: this.name, arn: null, value: null };
79
83
  }
80
84
  const result = await client.send(new CreateSecretCommand({
@@ -100,7 +104,11 @@ export class SecretsBuilder extends BaseBuilder {
100
104
  }
101
105
  }
102
106
  await this.deploySidecars();
103
- return { name: this.name, arn: this.resolvedArn, value: this.resolvedValue };
107
+ return {
108
+ name: this.name,
109
+ arn: this.resolvedArn,
110
+ value: this.resolvedValue,
111
+ };
104
112
  }
105
113
  async destroy() {
106
114
  const dryRun = this.isDryRunActive();
@@ -110,14 +118,14 @@ export class SecretsBuilder extends BaseBuilder {
110
118
  if (this._pendingDeletion)
111
119
  console.log(` ⏳ Secret "${this.name}" is already scheduled for deletion`);
112
120
  else
113
- console.log(` ✅ Secret "${this.name}" does not exist nothing to do`);
121
+ console.log(` ✅ Secret "${this.name}" does not exist - nothing to do`);
114
122
  return { destroyed: this.name };
115
123
  }
116
124
  if (dryRun) {
117
125
  const mode = this._forceDelete
118
126
  ? "immediate"
119
127
  : "scheduled (30-day recovery window)";
120
- console.log(` 📝 [PLAN] Delete secret "${this.name}" ${mode}`);
128
+ console.log(` 📝 [PLAN] Delete secret "${this.name}" - ${mode}`);
121
129
  return { destroyed: this.name };
122
130
  }
123
131
  await getSecretsClient().send(new DeleteSecretCommand({
@@ -140,7 +148,7 @@ export async function resolveEnvVars(env) {
140
148
  if (v instanceof SecretsBuilder) {
141
149
  await v.awaitValue();
142
150
  if (v.resolvedValue === null)
143
- throw new Error(`Secret "${v.name}" has no value create it first or call .plainText()/.keyValue() in the stack`);
151
+ throw new Error(`Secret "${v.name}" has no value - create it first or call .plainText()/.keyValue() in the stack`);
144
152
  resolved[k] = v.resolvedValue;
145
153
  }
146
154
  else {
@@ -1,4 +1,4 @@
1
- import { BaseBuilder } from '../../core/resource.js';
1
+ import { BaseBuilder } from "../../core/resource.js";
2
2
  export declare class SQSBuilder extends BaseBuilder {
3
3
  private _fifo;
4
4
  private _deduplication;
@@ -1,6 +1,6 @@
1
- import { GetQueueUrlCommand, GetQueueAttributesCommand, CreateQueueCommand, SetQueueAttributesCommand, DeleteQueueCommand, QueueAttributeName, } from '@aws-sdk/client-sqs';
2
- import { BaseBuilder } from '../../core/resource.js';
3
- import { getSQSClient } from './api.js';
1
+ import { GetQueueUrlCommand, GetQueueAttributesCommand, CreateQueueCommand, SetQueueAttributesCommand, DeleteQueueCommand, QueueAttributeName, } from "@aws-sdk/client-sqs";
2
+ import { BaseBuilder } from "../../core/resource.js";
3
+ import { getSQSClient } from "./api.js";
4
4
  export class SQSBuilder extends BaseBuilder {
5
5
  _fifo = false;
6
6
  _deduplication = false;
@@ -17,11 +17,26 @@ export class SQSBuilder extends BaseBuilder {
17
17
  super(name);
18
18
  this.discoveryPromise = this.discoverQueue(name);
19
19
  }
20
- fifo(enabled = true) { this._fifo = enabled; return this; }
21
- deduplication(enabled = true) { this._deduplication = enabled; return this; }
22
- timeout(seconds) { this._visibilityTimeout = seconds; return this; }
23
- retention(days) { this._retentionDays = days; return this; }
24
- delay(seconds) { this._delaySeconds = seconds; return this; }
20
+ fifo(enabled = true) {
21
+ this._fifo = enabled;
22
+ return this;
23
+ }
24
+ deduplication(enabled = true) {
25
+ this._deduplication = enabled;
26
+ return this;
27
+ }
28
+ timeout(seconds) {
29
+ this._visibilityTimeout = seconds;
30
+ return this;
31
+ }
32
+ retention(days) {
33
+ this._retentionDays = days;
34
+ return this;
35
+ }
36
+ delay(seconds) {
37
+ this._delaySeconds = seconds;
38
+ return this;
39
+ }
25
40
  dlq(name, maxReceives = 3) {
26
41
  this._dlqName = name;
27
42
  this._dlqMaxReceives = maxReceives;
@@ -29,13 +44,13 @@ export class SQSBuilder extends BaseBuilder {
29
44
  }
30
45
  queueName() {
31
46
  const base = this.name;
32
- if (this._fifo && !base.endsWith('.fifo'))
47
+ if (this._fifo && !base.endsWith(".fifo"))
33
48
  return `${base}.fifo`;
34
49
  return base;
35
50
  }
36
51
  async discoverQueue(name) {
37
52
  const sqs = getSQSClient();
38
- const qName = this._fifo && !name.endsWith('.fifo') ? `${name}.fifo` : name;
53
+ const qName = this._fifo && !name.endsWith(".fifo") ? `${name}.fifo` : name;
39
54
  try {
40
55
  const urlResult = await sqs.send(new GetQueueUrlCommand({ QueueName: qName }));
41
56
  this.resolvedUrl = urlResult.QueueUrl;
@@ -47,9 +62,10 @@ export class SQSBuilder extends BaseBuilder {
47
62
  return { url: this.resolvedUrl, arn: this.resolvedArn };
48
63
  }
49
64
  catch (e) {
50
- if (e.name === 'QueueDoesNotExist' || e.name === 'AWS.SimpleQueueService.NonExistentQueue')
65
+ if (e.name === "QueueDoesNotExist" ||
66
+ e.name === "AWS.SimpleQueueService.NonExistentQueue")
51
67
  return null;
52
- if (e.name === 'CredentialsProviderError')
68
+ if (e.name === "CredentialsProviderError")
53
69
  return null;
54
70
  throw e;
55
71
  }
@@ -66,7 +82,8 @@ export class SQSBuilder extends BaseBuilder {
66
82
  return { url, arn: attrResult.Attributes?.QueueArn };
67
83
  }
68
84
  catch (e) {
69
- if (e.name !== 'QueueDoesNotExist' && e.name !== 'AWS.SimpleQueueService.NonExistentQueue')
85
+ if (e.name !== "QueueDoesNotExist" &&
86
+ e.name !== "AWS.SimpleQueueService.NonExistentQueue")
70
87
  throw e;
71
88
  }
72
89
  const created = await sqs.send(new CreateQueueCommand({ QueueName: queueName, Attributes: attrs }));
@@ -84,9 +101,9 @@ export class SQSBuilder extends BaseBuilder {
84
101
  DelaySeconds: String(this._delaySeconds),
85
102
  };
86
103
  if (this._fifo) {
87
- attrs.FifoQueue = 'true';
104
+ attrs.FifoQueue = "true";
88
105
  if (this._deduplication)
89
- attrs.ContentBasedDeduplication = 'true';
106
+ attrs.ContentBasedDeduplication = "true";
90
107
  }
91
108
  if (redrivePolicy)
92
109
  attrs.RedrivePolicy = redrivePolicy;
@@ -98,9 +115,13 @@ export class SQSBuilder extends BaseBuilder {
98
115
  const queueName = this.queueName();
99
116
  console.log(`\n⚡ Finalizing SQS Queue "${queueName}"...`);
100
117
  if (dryRun) {
101
- const dlqName = this._dlqName ? (this._fifo && !this._dlqName.endsWith('.fifo') ? `${this._dlqName}.fifo` : this._dlqName) : undefined;
102
- console.log(` 📝 [PLAN] ${existing ? 'Update' : 'Create'} queue "${queueName}"`);
103
- console.log(` └─ Type: ${this._fifo ? 'FIFO' : 'Standard'}`);
118
+ const dlqName = this._dlqName
119
+ ? this._fifo && !this._dlqName.endsWith(".fifo")
120
+ ? `${this._dlqName}.fifo`
121
+ : this._dlqName
122
+ : undefined;
123
+ console.log(` 📝 [PLAN] ${existing ? "Update" : "Create"} queue "${queueName}"`);
124
+ console.log(` └─ Type: ${this._fifo ? "FIFO" : "Standard"}`);
104
125
  console.log(` └─ Visibility timeout: ${this._visibilityTimeout}s | Retention: ${this._retentionDays}d`);
105
126
  if (this._delaySeconds)
106
127
  console.log(` └─ Delivery delay: ${this._delaySeconds}s`);
@@ -114,7 +135,7 @@ export class SQSBuilder extends BaseBuilder {
114
135
  // Create DLQ first so we have its ARN for the redrive policy
115
136
  let redrivePolicy;
116
137
  if (this._dlqName) {
117
- const dlqQueueName = this._fifo && !this._dlqName.endsWith('.fifo')
138
+ const dlqQueueName = this._fifo && !this._dlqName.endsWith(".fifo")
118
139
  ? `${this._dlqName}.fifo`
119
140
  : this._dlqName;
120
141
  const dlqAttrs = {
@@ -122,16 +143,19 @@ export class SQSBuilder extends BaseBuilder {
122
143
  MessageRetentionPeriod: String(this._retentionDays * 86400),
123
144
  };
124
145
  if (this._fifo)
125
- dlqAttrs.FifoQueue = 'true';
146
+ dlqAttrs.FifoQueue = "true";
126
147
  const dlq = await this.ensureQueue(dlqQueueName, dlqAttrs);
127
148
  this.resolvedDlqUrl = dlq.url;
128
149
  this.resolvedDlqArn = dlq.arn;
129
- redrivePolicy = JSON.stringify({ deadLetterTargetArn: dlq.arn, maxReceiveCount: this._dlqMaxReceives });
150
+ redrivePolicy = JSON.stringify({
151
+ deadLetterTargetArn: dlq.arn,
152
+ maxReceiveCount: this._dlqMaxReceives,
153
+ });
130
154
  console.log(` ✅ DLQ ready: ${dlqQueueName}`);
131
155
  }
132
156
  const attrs = this.buildAttributes(redrivePolicy);
133
157
  if (existing) {
134
- // FIFO queues reject attribute updates that change queue type only update mutable attrs
158
+ // FIFO queues reject attribute updates that change queue type - only update mutable attrs
135
159
  const mutableAttrs = {
136
160
  VisibilityTimeout: attrs.VisibilityTimeout,
137
161
  MessageRetentionPeriod: attrs.MessageRetentionPeriod,
@@ -164,7 +188,7 @@ export class SQSBuilder extends BaseBuilder {
164
188
  const existing = await this.discoveryPromise;
165
189
  console.log(`\n🗑️ Destroying SQS Queue "${this.queueName()}"...`);
166
190
  if (!existing) {
167
- console.log(` ✅ Queue "${this.queueName()}" does not exist nothing to do`);
191
+ console.log(` ✅ Queue "${this.queueName()}" does not exist - nothing to do`);
168
192
  return { destroyed: this.name };
169
193
  }
170
194
  if (dryRun) {
@@ -1,8 +1,8 @@
1
- import { BaseBuilder } from '../../core/resource.js';
2
- import { Output } from '../../core/output.js';
3
- import { DropletBuilder } from './droplet.js';
1
+ import { BaseBuilder } from "../../core/resource.js";
2
+ import { Output } from "../../core/output.js";
3
+ import { DropletBuilder } from "./droplet.js";
4
4
  export interface DNSRecord {
5
- type: 'A' | 'CNAME' | 'TXT' | 'MX';
5
+ type: "A" | "CNAME" | "TXT" | "MX";
6
6
  name: string;
7
7
  value: string | DropletBuilder | Output<string>;
8
8
  }
@@ -1,8 +1,8 @@
1
- import { BaseBuilder } from '../../core/resource.js';
2
- import { Output } from '../../core/output.js';
3
- import { DropletBuilder } from './droplet.js';
4
- import { CertificateBuilder } from './certificate.js';
5
- import { getDoApi } from './api.js';
1
+ import { BaseBuilder } from "../../core/resource.js";
2
+ import { Output } from "../../core/output.js";
3
+ import { DropletBuilder } from "./droplet.js";
4
+ import { CertificateBuilder } from "./certificate.js";
5
+ import { getDoApi } from "./api.js";
6
6
  export class DomainBuilder extends BaseBuilder {
7
7
  domainName;
8
8
  records = [];
@@ -14,7 +14,9 @@ export class DomainBuilder extends BaseBuilder {
14
14
  async discoverDomain(name) {
15
15
  const api = getDoApi();
16
16
  try {
17
- return await api.get(`/domains/${name}`).then(d => d.domain);
17
+ return await api
18
+ .get(`/domains/${name}`)
19
+ .then((d) => d.domain);
18
20
  }
19
21
  catch {
20
22
  return null;
@@ -26,11 +28,11 @@ export class DomainBuilder extends BaseBuilder {
26
28
  return this;
27
29
  }
28
30
  pointer(name, target) {
29
- this.records.push({ type: 'A', name, value: target });
31
+ this.records.push({ type: "A", name, value: target });
30
32
  return this;
31
33
  }
32
34
  cname(name, target) {
33
- this.records.push({ type: 'CNAME', name, value: target });
35
+ this.records.push({ type: "CNAME", name, value: target });
34
36
  return this;
35
37
  }
36
38
  async deploy() {
@@ -43,7 +45,7 @@ export class DomainBuilder extends BaseBuilder {
43
45
  console.log(` 📝 [PLAN] Create domain ${this.domainName}`);
44
46
  }
45
47
  else {
46
- await api.post('/domains', { name: this.domainName });
48
+ await api.post("/domains", { name: this.domainName });
47
49
  console.log(`🚀 Created domain ${this.domainName}`);
48
50
  }
49
51
  }
@@ -53,7 +55,9 @@ export class DomainBuilder extends BaseBuilder {
53
55
  data = await record.value.get();
54
56
  }
55
57
  else if (record.value instanceof DropletBuilder) {
56
- data = (await record.value.getPublicIp()) ?? `[IP of ${record.value.name} — not found]`;
58
+ data =
59
+ (await record.value.getPublicIp()) ??
60
+ `[IP of ${record.value.name} - not found]`;
57
61
  }
58
62
  else {
59
63
  data = record.value;
@@ -64,7 +68,7 @@ export class DomainBuilder extends BaseBuilder {
64
68
  }
65
69
  // Delete existing record with same type+name before creating
66
70
  const existing_records = await api.get(`/domains/${this.domainName}/records?per_page=200`);
67
- const dupe = existing_records.domain_records.find(r => r.type === record.type && r.name === record.name);
71
+ const dupe = existing_records.domain_records.find((r) => r.type === record.type && r.name === record.name);
68
72
  if (dupe)
69
73
  await api.delete(`/domains/${this.domainName}/records/${dupe.id}`);
70
74
  await api.post(`/domains/${this.domainName}/records`, {
@@ -1,4 +1,4 @@
1
- import { BaseBuilder } from '../../core/resource.js';
1
+ import { BaseBuilder } from "../../core/resource.js";
2
2
  interface OAuthCredentials {
3
3
  clientId: string;
4
4
  clientSecret: string;
@@ -1,38 +1,67 @@
1
- import { BaseBuilder } from '../../core/resource.js';
2
- import { cloudFetch, getProjectId } from './api.js';
3
- const AUTH_BASE = 'https://identitytoolkit.googleapis.com/admin/v2';
1
+ import { BaseBuilder } from "../../core/resource.js";
2
+ import { cloudFetch, getProjectId } from "./api.js";
3
+ const AUTH_BASE = "https://identitytoolkit.googleapis.com/admin/v2";
4
4
  const IDP_ID = {
5
- google: 'google.com',
6
- github: 'github.com',
7
- facebook: 'facebook.com',
8
- twitter: 'twitter.com',
9
- apple: 'apple.com',
10
- microsoft: 'microsoft.com',
5
+ google: "google.com",
6
+ github: "github.com",
7
+ facebook: "facebook.com",
8
+ twitter: "twitter.com",
9
+ apple: "apple.com",
10
+ microsoft: "microsoft.com",
11
11
  };
12
12
  export class FirebaseAuthBuilder extends BaseBuilder {
13
13
  _providers = {};
14
14
  _allowedDomains;
15
15
  _authorizedDomains;
16
16
  constructor() {
17
- super('auth');
17
+ super("auth");
18
18
  this.discoveryPromise = Promise.resolve(null);
19
19
  }
20
20
  emailPassword(opts = {}) {
21
21
  this._providers.emailPassword = { enabled: true, ...opts };
22
22
  return this;
23
23
  }
24
- anonymous() { this._providers.anonymous = { enabled: true }; return this; }
25
- phone() { this._providers.phone = { enabled: true }; return this; }
26
- google(creds) { this._providers.google = { enabled: true, ...creds }; return this; }
27
- github(creds) { this._providers.github = { enabled: true, ...creds }; return this; }
28
- facebook(creds) { this._providers.facebook = { enabled: true, ...creds }; return this; }
29
- twitter(creds) { this._providers.twitter = { enabled: true, ...creds }; return this; }
30
- apple(creds) { this._providers.apple = { enabled: true, ...creds }; return this; }
31
- microsoft(creds) { this._providers.microsoft = { enabled: true, ...creds }; return this; }
24
+ anonymous() {
25
+ this._providers.anonymous = { enabled: true };
26
+ return this;
27
+ }
28
+ phone() {
29
+ this._providers.phone = { enabled: true };
30
+ return this;
31
+ }
32
+ google(creds) {
33
+ this._providers.google = { enabled: true, ...creds };
34
+ return this;
35
+ }
36
+ github(creds) {
37
+ this._providers.github = { enabled: true, ...creds };
38
+ return this;
39
+ }
40
+ facebook(creds) {
41
+ this._providers.facebook = { enabled: true, ...creds };
42
+ return this;
43
+ }
44
+ twitter(creds) {
45
+ this._providers.twitter = { enabled: true, ...creds };
46
+ return this;
47
+ }
48
+ apple(creds) {
49
+ this._providers.apple = { enabled: true, ...creds };
50
+ return this;
51
+ }
52
+ microsoft(creds) {
53
+ this._providers.microsoft = { enabled: true, ...creds };
54
+ return this;
55
+ }
32
56
  // Domains allowed to use Firebase Auth (e.g. your app domain + localhost)
33
- authorizedDomains(domains) { this._authorizedDomains = domains; return this; }
57
+ authorizedDomains(domains) {
58
+ this._authorizedDomains = domains;
59
+ return this;
60
+ }
34
61
  // ── Helpers ───────────────────────────────────────────────────────────────
35
- projectPath() { return `/projects/${getProjectId()}`; }
62
+ projectPath() {
63
+ return `/projects/${getProjectId()}`;
64
+ }
36
65
  async getConfig() {
37
66
  try {
38
67
  return await cloudFetch(AUTH_BASE, `${this.projectPath()}/config`);
@@ -42,7 +71,7 @@ export class FirebaseAuthBuilder extends BaseBuilder {
42
71
  }
43
72
  }
44
73
  async patchConfig(body, updateMask) {
45
- await cloudFetch(AUTH_BASE, `${this.projectPath()}/config?updateMask=${updateMask}`, { method: 'PATCH', body: JSON.stringify(body) });
74
+ await cloudFetch(AUTH_BASE, `${this.projectPath()}/config?updateMask=${updateMask}`, { method: "PATCH", body: JSON.stringify(body) });
46
75
  }
47
76
  async getIdp(provider) {
48
77
  try {
@@ -62,10 +91,10 @@ export class FirebaseAuthBuilder extends BaseBuilder {
62
91
  enabled: creds.enabled ?? true,
63
92
  };
64
93
  if (existing) {
65
- await cloudFetch(AUTH_BASE, `${this.projectPath()}/defaultSupportedIdpConfigs/${idpId}?updateMask=clientId,clientSecret,enabled`, { method: 'PATCH', body: JSON.stringify(body) });
94
+ await cloudFetch(AUTH_BASE, `${this.projectPath()}/defaultSupportedIdpConfigs/${idpId}?updateMask=clientId,clientSecret,enabled`, { method: "PATCH", body: JSON.stringify(body) });
66
95
  }
67
96
  else {
68
- await cloudFetch(AUTH_BASE, `${this.projectPath()}/defaultSupportedIdpConfigs?idpId=${idpId}`, { method: 'POST', body: JSON.stringify(body) });
97
+ await cloudFetch(AUTH_BASE, `${this.projectPath()}/defaultSupportedIdpConfigs?idpId=${idpId}`, { method: "POST", body: JSON.stringify(body) });
69
98
  }
70
99
  }
71
100
  // ── Deploy ────────────────────────────────────────────────────────────────
@@ -89,7 +118,7 @@ export class FirebaseAuthBuilder extends BaseBuilder {
89
118
  console.log(` 📝 [PLAN] Enable OAuth provider: ${IDP_ID[name]}`);
90
119
  }
91
120
  if (this._authorizedDomains) {
92
- console.log(` 📝 [PLAN] Set authorized domains: [${this._authorizedDomains.join(', ')}]`);
121
+ console.log(` 📝 [PLAN] Set authorized domains: [${this._authorizedDomains.join(", ")}]`);
93
122
  }
94
123
  return { project: getProjectId() };
95
124
  }
@@ -98,25 +127,28 @@ export class FirebaseAuthBuilder extends BaseBuilder {
98
127
  const signIn = {};
99
128
  const masks = [];
100
129
  if (emailPassword) {
101
- signIn.email = { enabled: true, passwordRequired: emailPassword.passwordRequired ?? true };
102
- masks.push('signIn.email');
130
+ signIn.email = {
131
+ enabled: true,
132
+ passwordRequired: emailPassword.passwordRequired ?? true,
133
+ };
134
+ masks.push("signIn.email");
103
135
  }
104
136
  if (anonymous) {
105
137
  signIn.anonymous = { enabled: true };
106
- masks.push('signIn.anonymous');
138
+ masks.push("signIn.anonymous");
107
139
  }
108
140
  if (phone) {
109
141
  signIn.phoneNumber = { enabled: true };
110
- masks.push('signIn.phoneNumber');
142
+ masks.push("signIn.phoneNumber");
111
143
  }
112
144
  const body = {};
113
145
  if (masks.length)
114
146
  body.signIn = signIn;
115
147
  if (this._authorizedDomains) {
116
148
  body.authorizedDomains = this._authorizedDomains;
117
- masks.push('authorizedDomains');
149
+ masks.push("authorizedDomains");
118
150
  }
119
- await this.patchConfig(body, masks.join(','));
151
+ await this.patchConfig(body, masks.join(","));
120
152
  if (emailPassword)
121
153
  console.log(` ✅ Email/Password enabled`);
122
154
  if (anonymous)
@@ -124,7 +156,7 @@ export class FirebaseAuthBuilder extends BaseBuilder {
124
156
  if (phone)
125
157
  console.log(` ✅ Phone sign-in enabled`);
126
158
  if (this._authorizedDomains)
127
- console.log(` ✅ Authorized domains set: [${this._authorizedDomains.join(', ')}]`);
159
+ console.log(` ✅ Authorized domains set: [${this._authorizedDomains.join(", ")}]`);
128
160
  }
129
161
  // OAuth providers
130
162
  for (const [name, creds] of oauthEntries) {
@@ -137,11 +169,11 @@ export class FirebaseAuthBuilder extends BaseBuilder {
137
169
  const dryRun = this.isDryRunActive();
138
170
  console.log(`\n🗑️ Destroying Firebase Auth config...`);
139
171
  if (dryRun) {
140
- console.log(` ℹ️ Auth providers can be disabled individually destroying the Auth config is not supported via API`);
172
+ console.log(` ℹ️ Auth providers can be disabled individually - destroying the Auth config is not supported via API`);
141
173
  }
142
174
  else {
143
175
  console.log(` ℹ️ Disable providers individually in the Firebase console`);
144
176
  }
145
- return { destroyed: 'auth' };
177
+ return { destroyed: "auth" };
146
178
  }
147
179
  }
@@ -1,5 +1,5 @@
1
- import { BaseBuilder } from '../../core/resource.js';
2
- type FieldOrder = 'ASCENDING' | 'DESCENDING';
1
+ import { BaseBuilder } from "../../core/resource.js";
2
+ type FieldOrder = "ASCENDING" | "DESCENDING";
3
3
  interface IndexField {
4
4
  field: string;
5
5
  order: FieldOrder;
@@ -1,17 +1,23 @@
1
- import { readFileSync } from 'node:fs';
2
- import { BaseBuilder } from '../../core/resource.js';
3
- import { cloudFetch, getProjectId } from './api.js';
4
- const RULES_BASE = 'https://firebaserules.googleapis.com/v1';
5
- const FS_BASE = 'https://firestore.googleapis.com/v1';
1
+ import { readFileSync } from "node:fs";
2
+ import { BaseBuilder } from "../../core/resource.js";
3
+ import { cloudFetch, getProjectId } from "./api.js";
4
+ const RULES_BASE = "https://firebaserules.googleapis.com/v1";
5
+ const FS_BASE = "https://firestore.googleapis.com/v1";
6
6
  export class FirebaseFirestoreBuilder extends BaseBuilder {
7
7
  _rulesPath;
8
8
  _indexes = [];
9
- constructor(database = '(default)') {
9
+ constructor(database = "(default)") {
10
10
  super(database);
11
11
  this.discoveryPromise = Promise.resolve(null);
12
12
  }
13
- rules(filePath) { this._rulesPath = filePath; return this; }
14
- index(collection, fields) { this._indexes.push({ collection, fields }); return this; }
13
+ rules(filePath) {
14
+ this._rulesPath = filePath;
15
+ return this;
16
+ }
17
+ index(collection, fields) {
18
+ this._indexes.push({ collection, fields });
19
+ return this;
20
+ }
15
21
  // ── Rules ────────────────────────────────────────────────────────────────
16
22
  rulesRelease() {
17
23
  return `projects/${getProjectId()}/releases/cloud.firestore`;
@@ -28,23 +34,28 @@ export class FirebaseFirestoreBuilder extends BaseBuilder {
28
34
  async deployRules(dryRun) {
29
35
  if (!this._rulesPath)
30
36
  return;
31
- const source = readFileSync(this._rulesPath, 'utf8');
37
+ const source = readFileSync(this._rulesPath, "utf8");
32
38
  const current = await this.currentRulesReleaseRuleset();
33
39
  if (dryRun) {
34
40
  console.log(` 📝 [PLAN] Deploy Firestore rules from "${this._rulesPath}"`);
35
41
  if (current)
36
- console.log(` └─ replaces ruleset: ${current.split('/').pop()}`);
42
+ console.log(` └─ replaces ruleset: ${current.split("/").pop()}`);
37
43
  return;
38
44
  }
39
45
  const ruleset = await cloudFetch(RULES_BASE, `/projects/${getProjectId()}/rulesets`, {
40
- method: 'POST',
41
- body: JSON.stringify({ source: { files: [{ name: 'firestore.rules', content: source }] } }),
46
+ method: "POST",
47
+ body: JSON.stringify({
48
+ source: { files: [{ name: "firestore.rules", content: source }] },
49
+ }),
42
50
  });
43
51
  await cloudFetch(RULES_BASE, `/${this.rulesRelease()}`, {
44
- method: 'PUT',
45
- body: JSON.stringify({ name: this.rulesRelease(), rulesetName: ruleset.name }),
52
+ method: "PUT",
53
+ body: JSON.stringify({
54
+ name: this.rulesRelease(),
55
+ rulesetName: ruleset.name,
56
+ }),
46
57
  });
47
- console.log(` ✅ Rules deployed (ruleset: ${ruleset.name.split('/').pop()})`);
58
+ console.log(` ✅ Rules deployed (ruleset: ${ruleset.name.split("/").pop()})`);
48
59
  }
49
60
  // ── Indexes ───────────────────────────────────────────────────────────────
50
61
  dbPath() {
@@ -60,37 +71,43 @@ export class FirebaseFirestoreBuilder extends BaseBuilder {
60
71
  }
61
72
  }
62
73
  indexKey(collection, fields) {
63
- return `${collection}:${fields.map(f => `${f.field}:${f.order}`).join(',')}`;
74
+ return `${collection}:${fields.map((f) => `${f.field}:${f.order}`).join(",")}`;
64
75
  }
65
76
  async deployIndexes(dryRun) {
66
77
  if (this._indexes.length === 0)
67
78
  return;
68
79
  const existing = await this.listExistingIndexes();
69
80
  const existingKeys = new Set(existing.map((idx) => {
70
- const parts = idx.name.split('/collectionGroups/');
71
- const collection = parts[1]?.split('/')[0] ?? '';
81
+ const parts = idx.name.split("/collectionGroups/");
82
+ const collection = parts[1]?.split("/")[0] ?? "";
72
83
  const fields = (idx.fields ?? [])
73
- .filter((f) => f.fieldPath !== '__name__')
74
- .map((f) => ({ field: f.fieldPath, order: f.order }));
84
+ .filter((f) => f.fieldPath !== "__name__")
85
+ .map((f) => ({
86
+ field: f.fieldPath,
87
+ order: f.order,
88
+ }));
75
89
  return this.indexKey(collection, fields);
76
90
  }));
77
- const toCreate = this._indexes.filter(i => !existingKeys.has(this.indexKey(i.collection, i.fields)));
91
+ const toCreate = this._indexes.filter((i) => !existingKeys.has(this.indexKey(i.collection, i.fields)));
78
92
  if (dryRun) {
79
93
  console.log(` 📝 [PLAN] ${toCreate.length} index(es) to create, ${this._indexes.length - toCreate.length} already exist`);
80
94
  for (const idx of toCreate) {
81
- console.log(` └─ ${idx.collection}: [${idx.fields.map(f => `${f.field} ${f.order}`).join(', ')}]`);
95
+ console.log(` └─ ${idx.collection}: [${idx.fields.map((f) => `${f.field} ${f.order}`).join(", ")}]`);
82
96
  }
83
97
  return;
84
98
  }
85
99
  for (const idx of toCreate) {
86
100
  await cloudFetch(FS_BASE, `/${this.dbPath()}/collectionGroups/${idx.collection}/indexes`, {
87
- method: 'POST',
101
+ method: "POST",
88
102
  body: JSON.stringify({
89
- queryScope: 'COLLECTION',
90
- fields: idx.fields.map(f => ({ fieldPath: f.field, order: f.order })),
103
+ queryScope: "COLLECTION",
104
+ fields: idx.fields.map((f) => ({
105
+ fieldPath: f.field,
106
+ order: f.order,
107
+ })),
91
108
  }),
92
109
  });
93
- console.log(` ✅ Index created: ${idx.collection} [${idx.fields.map(f => `${f.field} ${f.order}`).join(', ')}]`);
110
+ console.log(` ✅ Index created: ${idx.collection} [${idx.fields.map((f) => `${f.field} ${f.order}`).join(", ")}]`);
94
111
  }
95
112
  if (toCreate.length === 0)
96
113
  console.log(` ✅ All indexes already exist`);
@@ -106,10 +123,10 @@ export class FirebaseFirestoreBuilder extends BaseBuilder {
106
123
  async destroy() {
107
124
  const dryRun = this.isDryRunActive();
108
125
  console.log(`\n🗑️ Destroying Firestore config "${this.name}"...`);
109
- // Firestore databases themselves cannot be deleted via API only the config managed here
126
+ // Firestore databases themselves cannot be deleted via API - only the config managed here
110
127
  if (dryRun) {
111
128
  if (this._rulesPath)
112
- console.log(` 📝 [PLAN] Rules release cannot be rolled back via API do this in the Firebase console`);
129
+ console.log(` 📝 [PLAN] Rules release cannot be rolled back via API - do this in the Firebase console`);
113
130
  console.log(` ℹ️ Firestore databases cannot be deleted via API`);
114
131
  }
115
132
  else {
@@ -1,4 +1,4 @@
1
- import { BaseBuilder } from '../../core/resource.js';
1
+ import { BaseBuilder } from "../../core/resource.js";
2
2
  export declare const FUNCTIONS_RUNTIME: {
3
3
  readonly NODEJS_22: "nodejs22";
4
4
  readonly NODEJS_20: "nodejs20";