@stoker-platform/cli 0.5.12

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 (94) hide show
  1. package/LICENSE.md +102 -0
  2. package/init-files/.##gitignore## +102 -0
  3. package/init-files/.devcontainer/devcontainer.json +14 -0
  4. package/init-files/.env/.env +70 -0
  5. package/init-files/.eslintrc.cjs +35 -0
  6. package/init-files/.firebaserc +6 -0
  7. package/init-files/.prettierignore +86 -0
  8. package/init-files/.prettierrc +5 -0
  9. package/init-files/bin/build.js +221 -0
  10. package/init-files/bin/shim.js +159 -0
  11. package/init-files/extensions/firestore-send-email.env +7 -0
  12. package/init-files/external.package.json +4 -0
  13. package/init-files/firebase-rules/database.rules.json +9 -0
  14. package/init-files/firebase-rules/firestore.custom.rules +19 -0
  15. package/init-files/firebase-rules/firestore.indexes.json +0 -0
  16. package/init-files/firebase-rules/firestore.rules +0 -0
  17. package/init-files/firebase-rules/storage.rules +0 -0
  18. package/init-files/firebase.hosting.json +122 -0
  19. package/init-files/firebase.json +52 -0
  20. package/init-files/functions/.##gitignore## +14 -0
  21. package/init-files/functions/.eslintrc.cjs +28 -0
  22. package/init-files/functions/package.json +46 -0
  23. package/init-files/functions/prompts/chat.prompt +17 -0
  24. package/init-files/functions/src/index.ts +457 -0
  25. package/init-files/functions/tsconfig.dev.json +3 -0
  26. package/init-files/functions/tsconfig.json +17 -0
  27. package/init-files/icons/logo-large.png +0 -0
  28. package/init-files/icons/logo-small.png +0 -0
  29. package/init-files/ops.js +25 -0
  30. package/init-files/package.json +53 -0
  31. package/init-files/project-data.json +5 -0
  32. package/init-files/remoteconfig.template.json +1 -0
  33. package/init-files/src/collections/Inbox.ts +444 -0
  34. package/init-files/src/collections/Outbox.ts +270 -0
  35. package/init-files/src/collections/Settings.ts +44 -0
  36. package/init-files/src/collections/Users.ts +138 -0
  37. package/init-files/src/main.ts +245 -0
  38. package/init-files/src/utils.ts +3 -0
  39. package/init-files/src/vite-env.d.ts +1 -0
  40. package/init-files/test/test.ts +5 -0
  41. package/init-files/tsconfig.json +23 -0
  42. package/init-files/vitest.config.ts +9 -0
  43. package/lib/package.json +45 -0
  44. package/lib/src/data/exportToBigQuery.js +41 -0
  45. package/lib/src/data/seedData.js +347 -0
  46. package/lib/src/deploy/applySchema.js +43 -0
  47. package/lib/src/deploy/cloud-functions/getFunctionsData.js +18 -0
  48. package/lib/src/deploy/deployProject.js +116 -0
  49. package/lib/src/deploy/firestore-export/exportFirestoreData.js +29 -0
  50. package/lib/src/deploy/firestore-ttl/deployTTLs.js +127 -0
  51. package/lib/src/deploy/live-update/liveUpdate.js +22 -0
  52. package/lib/src/deploy/maintenance/activateMaintenanceMode.js +9 -0
  53. package/lib/src/deploy/maintenance/disableMaintenanceMode.js +9 -0
  54. package/lib/src/deploy/maintenance/setDeploymentStatus.js +22 -0
  55. package/lib/src/deploy/rules-indexes/generateFirestoreIndexes.js +23 -0
  56. package/lib/src/deploy/rules-indexes/generateFirestoreRules.js +35 -0
  57. package/lib/src/deploy/rules-indexes/generateStorageRules.js +23 -0
  58. package/lib/src/deploy/schema/generateSchema.js +184 -0
  59. package/lib/src/deploy/schema/persistSchema.js +14 -0
  60. package/lib/src/lint/lintSchema.js +1491 -0
  61. package/lib/src/lint/securityReport.js +223 -0
  62. package/lib/src/main.js +460 -0
  63. package/lib/src/migration/firestore/migrateFirestore.js +8 -0
  64. package/lib/src/migration/firestore/operations/deleteField.js +58 -0
  65. package/lib/src/migration/migrateAll.js +30 -0
  66. package/lib/src/ops/auditDenormalized.js +124 -0
  67. package/lib/src/ops/auditPermissions.js +92 -0
  68. package/lib/src/ops/auditRelations.js +186 -0
  69. package/lib/src/ops/explainPreloadQueries.js +65 -0
  70. package/lib/src/ops/getUser.js +10 -0
  71. package/lib/src/ops/getUserPermissions.js +19 -0
  72. package/lib/src/ops/getUserRecord.js +20 -0
  73. package/lib/src/ops/listProjects.js +8 -0
  74. package/lib/src/ops/setUserCollection.js +14 -0
  75. package/lib/src/ops/setUserDocument.js +11 -0
  76. package/lib/src/ops/setUserRole.js +14 -0
  77. package/lib/src/project/addProject.js +935 -0
  78. package/lib/src/project/addRecord.js +9 -0
  79. package/lib/src/project/addRecordPrompt.js +205 -0
  80. package/lib/src/project/addTenant.js +59 -0
  81. package/lib/src/project/buildWebApp.js +10 -0
  82. package/lib/src/project/customDomain.js +157 -0
  83. package/lib/src/project/deleteProject.js +51 -0
  84. package/lib/src/project/deleteRecord.js +11 -0
  85. package/lib/src/project/deleteTenant.js +49 -0
  86. package/lib/src/project/getOne.js +25 -0
  87. package/lib/src/project/getSome.js +28 -0
  88. package/lib/src/project/initProject.js +16 -0
  89. package/lib/src/project/prepareEmulatorData.js +125 -0
  90. package/lib/src/project/setProject.js +13 -0
  91. package/lib/src/project/startEmulators.js +30 -0
  92. package/lib/src/project/updateRecord.js +9 -0
  93. package/lib/tsconfig.tsbuildinfo +1 -0
  94. package/package.json +45 -0
package/LICENSE.md ADDED
@@ -0,0 +1,102 @@
1
+ # Source-Available Development License
2
+
3
+ Copyright © 2026 Shopfront International Pty Ltd.
4
+
5
+ This is a source‑available license and not an OSI‑approved open‑source license.
6
+
7
+ ## 1. License Grant
8
+
9
+ Subject to the terms of this License, you are granted a **non-exclusive, non-transferable, revocable** license to:
10
+
11
+ - Access the Software.
12
+ - Use the Software **in development environments only**. Development environments include local development, testing, staging, and QA environments that are not accessible to end users.
13
+
14
+ This use is **free of charge**.
15
+
16
+ No other rights are granted except as expressly stated in this License.
17
+
18
+ ## 2. Commercial or Production Use
19
+
20
+ **Commercial use or production deployment is strictly prohibited** under this License. This includes, but is not limited to:
21
+
22
+ - Deploying the Software in production environments
23
+ - Using the Software in any revenue-generating product, service, or project
24
+ - Selling, sublicensing, or monetising the Software
25
+
26
+ You may not use the Software, directly or indirectly, to develop, distribute, operate, or enable any product or service that:
27
+
28
+ - #### Competes with the Software
29
+
30
+ By functioning as a headless CMS, application framework, developer platform, or substantially similar system.
31
+
32
+ - #### Replicates Core Functionality
33
+
34
+ Copies or substantially mirrors the Software’s architecture, workflows, abstractions, or core features.
35
+
36
+ - #### Offers the Software as a Service
37
+
38
+ Provides the Software itself or its functionality to third parties as a managed service, hosted platform, API, or marketplace.
39
+
40
+ - #### Rebrands or White-Labels the Software
41
+
42
+ Offers the Software itself under a different name, branding, or identity.
43
+
44
+ - #### Creates Derived Frameworks or Tooling
45
+
46
+ Distributes frameworks, starter kits, templates, scaffolding, or developer toolkits substantially derived from the Software.
47
+
48
+ - #### Competitive Analysis and Cloning
49
+
50
+ Uses the Software mainly to analyse, benchmark, reverse engineer, or replicate it for a competing product or service.
51
+
52
+ - #### Artificial Intelligence and Machine Learning
53
+ Uses the Software itself to train, fine-tune, or improve artificial intelligence or machine learning models.
54
+
55
+ To use the Software commercially or in production, you must obtain a Commercial License from the Licensor. Contact info@getoutpost.com for licensing information.
56
+
57
+ ## 3. Contributions
58
+
59
+ You may fork or modify the Software for contribution purposes.
60
+
61
+ You may submit issues, bug reports, feature requests, and pull requests through public channels (such as GitHub) as a means of contributing to the Software. Modifications may not be otherwise shared or distributed except through such official contribution channels.
62
+
63
+ By submitting contributions, suggestions, or modifications to the Licensor in connection with the Software, you grant the Licensor a perpetual, worldwide, royalty-free, non-exclusive, fully transferable, and sublicensable license to use, modify, distribute, and otherwise exploit such contributions as part of the Software. You retain ownership of your contributions but grant these rights to enable the Licensor to incorporate and distribute your contributions as part of the Software.
64
+
65
+ ## 4. Ownership
66
+
67
+ The Software is **licensed, not sold**. All rights not expressly granted herein are retained by the Licensor.
68
+
69
+ All trademarks, service marks, and logos associated with the Software are the property of the Licensor and may not be used without permission.
70
+
71
+ ## 5. Alpha Mode
72
+
73
+ - The Software may be provided in alpha or pre-release form. Features, functionality, and data handling may change without notice.
74
+ - By using the Software in alpha, you acknowledge it may contain bugs, errors, or incomplete functionality.
75
+ - The Licensor makes no commitment to maintain backward compatibility or preserve data formats during the alpha period.
76
+ - Alpha releases may be discontinued or replaced without prior notice.
77
+ - You are responsible for backing up your data and testing compatibility before upgrading.
78
+
79
+ ## 6. Limited Warranty / Liability
80
+
81
+ - To the extent permitted by law, the Software is provided **"AS IS"**, without warranties of any kind, express or implied, including but not limited to warranties of merchantability, fitness for a particular purpose, or non-infringement.
82
+ - Licensor is not liable for any damages arising from use or inability to use the Software, to the maximum extent permitted by law.
83
+ - Nothing in this License excludes rights or remedies that cannot be waived under applicable law.
84
+
85
+ ## 7. Termination
86
+
87
+ This License automatically terminates if you fail to comply with any term or condition. In this case, you must immediately stop using the Software.
88
+
89
+ **The licensor may terminate this license at any time**, for any reason, when 60 days notice is given on the Licensor's website at https://stoker-website.web.app. In this case, you will need to purchase a commerical license to continue using the Software.
90
+
91
+ The Licensor reserves the right to pursue legal remedies for any violation, including injunctive relief.
92
+
93
+ ## 8. Governing Law
94
+
95
+ This License is governed by and shall be construed under the laws of Victoria, Australia, without regard to conflict-of-law principles.
96
+
97
+ Any disputes arising under this License shall be resolved exclusively in the courts of Victoria, Australia.
98
+
99
+ ## 9. Commercial License
100
+
101
+ For commercial use or production deployment, a Commercial License Agreement must be obtained.
102
+ Please contact info@getoutpost.com for details.
@@ -0,0 +1,102 @@
1
+ # Logs
2
+ logs
3
+ *.log
4
+ npm-debug.log*
5
+ yarn-debug.log*
6
+ yarn-error.log*
7
+ pnpm-debug.log*
8
+ lerna-debug.log*
9
+ firebase-debug.log*
10
+ firebase-debug.*.log*
11
+
12
+ # Firebase cache
13
+ .firebase/
14
+
15
+ # Firebase config
16
+
17
+ # Uncomment this if you'd like others to create their own Firebase project.
18
+ # For a team working on the same Firebase project(s), it is recommended to leave
19
+ # it commented so all members can deploy to the same project(s) in .firebaserc.
20
+ # .firebaserc
21
+
22
+ # Runtime data
23
+ pids
24
+ *.pid
25
+ *.seed
26
+ *.pid.lock
27
+
28
+ # Editor directories and files
29
+ .vscode/*
30
+ !.vscode/extensions.json
31
+ .idea
32
+ .DS_Store
33
+ *.suo
34
+ *.ntvs*
35
+ *.njsproj
36
+ *.sln
37
+ *.sw?
38
+
39
+ # Directory for instrumented libs generated by jscoverage/JSCover
40
+ lib-cov
41
+
42
+ # Coverage directory used by tools like istanbul
43
+ coverage
44
+
45
+ # nyc test coverage
46
+ .nyc_output
47
+
48
+ # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
49
+ .grunt
50
+
51
+ # Bower dependency directory (https://bower.io/)
52
+ bower_components
53
+
54
+ # node-waf configuration
55
+ .lock-wscript
56
+
57
+ # Compiled binary addons (http://nodejs.org/api/addons.html)
58
+ build/Release
59
+
60
+ # Dependency directories
61
+ node_modules/
62
+
63
+ # Optional npm cache directory
64
+ .npm
65
+
66
+ # npm config file
67
+ .npmrc
68
+
69
+
70
+ # Optional eslint cache
71
+ .eslintcache
72
+
73
+ # Optional REPL history
74
+ .node_repl_history
75
+
76
+ # Output of 'npm pack'
77
+ *.tgz
78
+
79
+ # Yarn Integrity file
80
+ .yarn-integrity
81
+
82
+ # Turborepo configuration
83
+ .turbo
84
+
85
+ # Build directories
86
+ lib
87
+ dist
88
+ dist-ssr
89
+ *.local
90
+
91
+ # Dotenv variables
92
+ .env
93
+ .env.*
94
+
95
+ # Firebase GenKit directory
96
+ .genkit
97
+
98
+ # Firebase emulator data
99
+ firebase-emulator-data/*
100
+
101
+ # Web app directory
102
+ web-app
@@ -0,0 +1,14 @@
1
+ {
2
+ "image": "mcr.microsoft.com/devcontainers/universal:2",
3
+ "features": {
4
+ "ghcr.io/dhoeric/features/google-cloud-cli:1": {
5
+ "version": "latest"
6
+ },
7
+ "ghcr.io/devcontainers-contrib/features/firebase-cli:2": {
8
+ "version": "latest"
9
+ },
10
+ "ghcr.io/devcontainers-contrib/features/typescript:2": {
11
+ "version": "latest"
12
+ }
13
+ }
14
+ }
@@ -0,0 +1,70 @@
1
+ ADMIN_EMAIL=""
2
+ ADMIN_SMS=""
3
+
4
+ GCP_BILLING_ACCOUNT=""
5
+
6
+ GCP_ORGANIZATION=""
7
+ GCP_FOLDER=""
8
+
9
+ FB_GOOGLE_ANALYTICS_ACCOUNT_ID=""
10
+
11
+ FB_FIRESTORE_REGION="australia-southeast2"
12
+ FB_FIRESTORE_ENABLE_PITR=true
13
+ FB_FIRESTORE_BACKUP_RECURRENCE="daily"
14
+ FB_FIRESTORE_BACKUP_RETENTION="7d"
15
+
16
+ FB_DATABASE_REGION="asia-southeast1"
17
+
18
+ FB_STORAGE_REGION="australia-southeast2"
19
+ FB_STORAGE_ENABLE_VERSIONING=true
20
+ FB_STORAGE_SOFT_DELETE_DURATION="30d"
21
+
22
+ FB_AUTH_PASSWORD_POLICY='{
23
+ "containsLowercaseCharacter": false,
24
+ "containsUppercaseCharacter": false,
25
+ "containsNumericCharacter": false,
26
+ "containsNonAlphanumericCharacter": false,
27
+ "minPasswordLength": 12,
28
+ "maxPasswordLength": 128
29
+ }'
30
+ FB_AUTH_PASSWORD_POLICY_UPGRADE=false
31
+
32
+ FB_FUNCTIONS_REGION="australia-southeast2"
33
+ FB_FUNCTIONS_V1_REGION="australia-southeast1"
34
+ FB_FUNCTIONS_MEMORY="1GiB"
35
+ FB_FUNCTIONS_TIMEOUT=540
36
+ FB_FUNCTIONS_MAX_INSTANCES=100
37
+ FB_FUNCTIONS_MIN_INSTANCES=0
38
+ FB_FUNCTIONS_CPU=1
39
+ FB_FUNCTIONS_CONCURRENCY=80
40
+ FB_FUNCTIONS_CONSUME_APP_CHECK_TOKEN=false
41
+
42
+ FB_HOSTING_ENABLE_CLOUD_LOGGING=true
43
+ FB_HOSTING_MAX_VERSIONS=3
44
+
45
+ FB_ENABLE_APP_CHECK=true
46
+ FB_APP_CHECK_TOKEN_TTL="3600s"
47
+
48
+ FB_AI_REGION="us-west1"
49
+
50
+ MAIL_REGION="asia-northeast1"
51
+ MAIL_SENDER=""
52
+ MAIL_SMTP_CONNECTION_URI=""
53
+ MAIL_SMTP_PASSWORD=""
54
+
55
+ TWILIO_ACCOUNT_SID=""
56
+ TWILIO_AUTH_TOKEN=""
57
+ TWILIO_PHONE_NUMBER=""
58
+
59
+ ALGOLIA_ID=""
60
+ ALGOLIA_ADMIN_KEY=""
61
+
62
+ SENTRY_DSN=""
63
+
64
+ FULLCALENDAR_KEY=""
65
+
66
+ EXTERNAL_SECRETS='{}'
67
+
68
+ URL_FIRESTORE_INDEXES="https://australia-southeast2-stoker-backend-prod-v1.cloudfunctions.net/stoker-getfirestoreindexes"
69
+ URL_FIRESTORE_RULES="https://australia-southeast2-stoker-backend-prod-v1.cloudfunctions.net/stoker-getfirestorerules"
70
+ URL_STORAGE_RULES="https://australia-southeast2-stoker-backend-prod-v1.cloudfunctions.net/stoker-getstoragerules"
@@ -0,0 +1,35 @@
1
+ module.exports = {
2
+ env: {
3
+ es6: true,
4
+ node: true,
5
+ },
6
+ extends: [
7
+ "eslint:recommended",
8
+ "plugin:import/errors",
9
+ "plugin:import/warnings",
10
+ "plugin:import/typescript",
11
+ "plugin:@typescript-eslint/recommended",
12
+ "plugin:security/recommended-legacy",
13
+ "prettier",
14
+ ],
15
+ parser: "@typescript-eslint/parser",
16
+ parserOptions: {
17
+ project: ["tsconfig.json"],
18
+ sourceType: "module",
19
+ },
20
+ ignorePatterns: [
21
+ "/lib/**/*",
22
+ "/dist/**/*",
23
+ "bin/**/*",
24
+ "test/**/*",
25
+ "vitest.config.ts",
26
+ "/functions",
27
+ "/web-app",
28
+ "ops.js",
29
+ ],
30
+ plugins: ["@typescript-eslint", "import"],
31
+ rules: {
32
+ "import/no-unresolved": 0,
33
+ "prefer-rest-params": 0,
34
+ },
35
+ }
@@ -0,0 +1,6 @@
1
+ {
2
+ "projects": {},
3
+ "targets": {},
4
+ "etags": {},
5
+ "dataconnectEmulatorConfig": {}
6
+ }
@@ -0,0 +1,86 @@
1
+ # Logs
2
+ logs
3
+ *.log
4
+ npm-debug.log*
5
+ yarn-debug.log*
6
+ yarn-error.log*
7
+ pnpm-debug.log*
8
+ lerna-debug.log*
9
+ firebase-debug.log*
10
+ firebase-debug.*.log*
11
+
12
+ # Firebase cache
13
+ .firebase/
14
+
15
+ # Runtime data
16
+ pids
17
+ *.pid
18
+ *.seed
19
+ *.pid.lock
20
+
21
+ # Editor directories and files
22
+ .vscode/*
23
+ !.vscode/extensions.json
24
+ .idea
25
+ .DS_Store
26
+ *.suo
27
+ *.ntvs*
28
+ *.njsproj
29
+ *.sln
30
+ *.sw?
31
+
32
+ # Directory for instrumented libs generated by jscoverage/JSCover
33
+ lib-cov
34
+
35
+ # Coverage directory used by tools like istanbul
36
+ coverage
37
+
38
+ # nyc test coverage
39
+ .nyc_output
40
+
41
+ # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
42
+ .grunt
43
+
44
+ # Bower dependency directory (https://bower.io/)
45
+ bower_components
46
+
47
+ # node-waf configuration
48
+ .lock-wscript
49
+
50
+ # Compiled binary addons (http://nodejs.org/api/addons.html)
51
+ build/Release
52
+
53
+ # Optional npm cache directory
54
+ .npm
55
+
56
+ # Optional eslint cache
57
+ .eslintcache
58
+
59
+ # Optional REPL history
60
+ .node_repl_history
61
+
62
+ # Output of 'npm pack'
63
+ *.tgz
64
+
65
+ # Yarn Integrity file
66
+ .yarn-integrity
67
+
68
+ # Turborepo configuration
69
+ .turbo
70
+
71
+ # Build directories
72
+ lib
73
+ dist
74
+ dist-ssr
75
+ *.local
76
+
77
+ # Firebase GenKit directory
78
+ .genkit
79
+
80
+ # Firebase directories
81
+
82
+ firebase-rules/
83
+ functions/src/
84
+ functions/.eslintrc.cjs
85
+
86
+ package-lock.json
@@ -0,0 +1,5 @@
1
+ {
2
+ "printWidth": 120,
3
+ "tabWidth": 4,
4
+ "semi": false
5
+ }
@@ -0,0 +1,221 @@
1
+ #!/usr/bin/env -S node --no-warnings
2
+
3
+ import { fileURLToPath } from "url"
4
+ import { dirname, join } from "path"
5
+ import { readFile, writeFile, readdir, mkdir, copyFile } from "fs/promises"
6
+ import { existsSync, cpSync, rmSync } from "fs"
7
+ import dotenv from "dotenv"
8
+ import { runChildProcess } from "@stoker-platform/node-client"
9
+
10
+ const envDir = join(process.cwd(), ".env")
11
+ const projectEnvFile = join(envDir, `.env.project.${process.env.GCP_PROJECT}`)
12
+ // eslint-disable-next-line security/detect-non-literal-fs-filename
13
+ if (existsSync(projectEnvFile)) {
14
+ dotenv.config({ path: projectEnvFile, quiet: true })
15
+ } else {
16
+ dotenv.config({ path: join(process.cwd(), ".env", ".env"), quiet: true })
17
+ }
18
+
19
+ try {
20
+ const __filename = fileURLToPath(import.meta.url)
21
+ const __dirname = dirname(__filename)
22
+
23
+ // Remove CSS imports from all JS files in lib directory
24
+
25
+ async function processFile(filePath) {
26
+ try {
27
+ const content = await readFile(filePath, "utf8")
28
+ const lines = content.split("\n")
29
+ const filteredLines = lines.filter((line) => {
30
+ const trimmed = line.trim()
31
+ // Match: import "./something.css" or import './something.css'
32
+ if (/^import\s+["']\.\/.*\.css["'];?\s*$/.test(trimmed)) {
33
+ return false
34
+ }
35
+ // Match: import "something.css" or import 'something.css' (absolute imports)
36
+ if (/^import\s+["'].*\.css["'];?\s*$/.test(trimmed)) {
37
+ return false
38
+ }
39
+ return true
40
+ })
41
+
42
+ const newContent = filteredLines.join("\n")
43
+ if (newContent !== content) {
44
+ await writeFile(filePath, newContent, "utf8")
45
+ console.log(`Removed CSS imports from: ${filePath}`)
46
+ }
47
+ } catch (error) {
48
+ console.error(`Error processing ${filePath}:`, error.message)
49
+ }
50
+ }
51
+
52
+ async function walkDirectory(dir) {
53
+ try {
54
+ const entries = await readdir(dir, { withFileTypes: true })
55
+
56
+ for (const entry of entries) {
57
+ const fullPath = join(dir, entry.name)
58
+
59
+ if (entry.isDirectory()) {
60
+ await walkDirectory(fullPath)
61
+ } else if (entry.isFile() && entry.name.endsWith(".js")) {
62
+ await processFile(fullPath)
63
+ }
64
+ }
65
+ } catch (error) {
66
+ if (error.code !== "ENOENT") {
67
+ console.error(`Error walking directory ${dir}:`, error.message)
68
+ }
69
+ }
70
+ }
71
+
72
+ async function removeCSSImports() {
73
+ const libDir = join(__dirname, "..", "lib")
74
+ await walkDirectory(libDir)
75
+ }
76
+
77
+ await removeCSSImports().catch((error) => {
78
+ console.error("Error:", error)
79
+ process.exit(1)
80
+ })
81
+
82
+ // Set up Firebase Extension files
83
+
84
+ let envFile
85
+ if (existsSync(projectEnvFile)) {
86
+ envFile = await readFile(projectEnvFile, "utf8")
87
+ } else {
88
+ envFile = await readFile(join(__dirname, "..", ".env", ".env"), "utf8")
89
+ }
90
+ const envFileLines = envFile.split("\n")
91
+ const databaseRegion = envFileLines.find((line) => line.startsWith("FB_FIRESTORE_REGION="))
92
+
93
+ const mailRegion = envFileLines.find((line) => line.startsWith("MAIL_REGION="))
94
+ const mailSender = envFileLines.find((line) => line.startsWith("MAIL_SENDER="))
95
+ const mailSmtpConnectionUri = envFileLines.find((line) => line.startsWith("MAIL_SMTP_CONNECTION_URI="))
96
+
97
+ const extensionEnvFile = await readFile(join(__dirname, "..", "extensions", "firestore-send-email.env"), "utf8")
98
+ const extensionEnvFileLines = extensionEnvFile.split("\n")
99
+ const linesToRemove = [
100
+ "EVENTARC_CHANNEL=",
101
+ "DEFAULT_FROM=",
102
+ "DEFAULT_REPLY_TO=",
103
+ "SMTP_CONNECTION_URI=",
104
+ "firebaseextensions.v1beta.function/location=",
105
+ "DATABASE_REGION=",
106
+ ]
107
+ const filteredLines = extensionEnvFileLines.filter(
108
+ (line) => !linesToRemove.some((removeStr) => line.startsWith(removeStr)),
109
+ )
110
+ filteredLines.push(
111
+ `EVENTARC_CHANNEL=projects/\${param:PROJECT_ID}/locations/${mailRegion.split("=")[1].replace(/^"|"$/g, "")}/channels/firebase`,
112
+ )
113
+ filteredLines.push(`firebaseextensions.v1beta.function/location=${process.env.FB_FUNCTIONS_REGION}`)
114
+ filteredLines.push(`DEFAULT_FROM=${mailSender.split("=")[1].replace(/^"|"$/g, "")}`)
115
+ filteredLines.push(`DEFAULT_REPLY_TO=${mailSender.split("=")[1].replace(/^"|"$/g, "")}`)
116
+ filteredLines.push(`SMTP_CONNECTION_URI=${mailSmtpConnectionUri.split("=")[1].replace(/^"|"$/g, "")}`)
117
+ filteredLines.push(`DATABASE_REGION=${databaseRegion.split("=")[1].replace(/^"|"$/g, "")}`)
118
+ await writeFile(join(__dirname, "..", "extensions", "firestore-send-email.env"), filteredLines.join("\n"))
119
+
120
+ // Update dependencies according to external.package.json
121
+
122
+ const updateDependencies = async (packageJsonPath, externalDeps) => {
123
+ const packageJsonRaw = await readFile(packageJsonPath, "utf8")
124
+ const packageJson = JSON.parse(packageJsonRaw)
125
+
126
+ const dependencies = packageJson.dependencies || {}
127
+ const previouslyManaged = packageJson.stokerExternalPackages || {}
128
+
129
+ let changed = false
130
+
131
+ for (const managedName of Object.keys(previouslyManaged)) {
132
+ if (!Object.prototype.hasOwnProperty.call(externalDeps, managedName)) {
133
+ if (Object.prototype.hasOwnProperty.call(dependencies, managedName)) {
134
+ delete dependencies[managedName]
135
+ changed = true
136
+ }
137
+ }
138
+ }
139
+
140
+ for (const [depName, depVersion] of Object.entries(externalDeps)) {
141
+ if (dependencies[depName] !== depVersion) {
142
+ dependencies[depName] = depVersion
143
+ changed = true
144
+ }
145
+ }
146
+
147
+ packageJson.dependencies = dependencies
148
+ packageJson.stokerExternalPackages = externalDeps
149
+
150
+ if (changed || JSON.stringify(previouslyManaged) !== JSON.stringify(externalDeps)) {
151
+ await writeFile(packageJsonPath, JSON.stringify(packageJson, null, 4) + "\n")
152
+ }
153
+
154
+ return changed
155
+ }
156
+
157
+ const externalPackagesPath = join(process.cwd(), "external.package.json")
158
+ if (existsSync(externalPackagesPath)) {
159
+ const externalRaw = await readFile(externalPackagesPath, "utf8")
160
+ const external = JSON.parse(externalRaw)
161
+ const serverDeps = external.server || {}
162
+ const webDeps = external.web || {}
163
+
164
+ const appDir = join(process.cwd())
165
+ const functionsDir = join(process.cwd(), "functions")
166
+
167
+ const appPackageJson = join(appDir, "package.json")
168
+ const functionsPackageJson = join(functionsDir, "package.json")
169
+
170
+ const appChanged = await updateDependencies(appPackageJson, { ...serverDeps, ...webDeps })
171
+ const functionsChanged = await updateDependencies(functionsPackageJson, serverDeps)
172
+
173
+ if (appChanged) {
174
+ await runChildProcess("npm", ["install", "--no-audit", "--no-fund"], appDir)
175
+ }
176
+ if (functionsChanged) {
177
+ await runChildProcess("npm", ["install", "--no-audit", "--no-fund"], functionsDir)
178
+ }
179
+ }
180
+
181
+ // Create functions .env file with filtered environment variables
182
+
183
+ const functionsEnvPath = join(process.cwd(), "functions", ".env")
184
+ const defaultEnvFile = join(envDir, ".env")
185
+ const projectSpecificEnvFile = join(envDir, `.env.${process.env.GCP_PROJECT}`)
186
+
187
+ let envContent = ""
188
+ const envPattern = /^(FB_FUNCTIONS_|FB_AI_REGION|STOKER_|ADMIN_)/
189
+
190
+ if (existsSync(projectEnvFile)) {
191
+ const projectEnvContent = await readFile(projectEnvFile, "utf8")
192
+ const projectSpecificContent = existsSync(projectSpecificEnvFile)
193
+ ? await readFile(projectSpecificEnvFile, "utf8")
194
+ : ""
195
+
196
+ const allLines = [...projectEnvContent.split("\n"), ...projectSpecificContent.split("\n")]
197
+ const filteredLines = allLines.filter((line) => envPattern.test(line.trim()))
198
+ envContent = filteredLines.join("\n")
199
+ } else {
200
+ const defaultContent = existsSync(defaultEnvFile) ? await readFile(defaultEnvFile, "utf8") : ""
201
+ const projectSpecificContent = existsSync(projectSpecificEnvFile)
202
+ ? await readFile(projectSpecificEnvFile, "utf8")
203
+ : ""
204
+
205
+ const allLines = [...defaultContent.split("\n"), ...projectSpecificContent.split("\n")]
206
+ const filteredLines = allLines.filter((line) => envPattern.test(line.trim()))
207
+ envContent = filteredLines.join("\n")
208
+ }
209
+
210
+ await writeFile(functionsEnvPath, envContent, "utf8")
211
+
212
+ // Copy source files to functions directory
213
+
214
+ const systemCustom = join(process.cwd(), "functions", "src", "system-custom")
215
+ if (existsSync(systemCustom)) {
216
+ rmSync(systemCustom, { recursive: true })
217
+ }
218
+ cpSync(join(process.cwd(), "src"), systemCustom, { recursive: true })
219
+ } catch (error) {
220
+ throw new Error(error)
221
+ }