@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
@@ -0,0 +1,457 @@
1
+ import {initializeApp} from "firebase-admin/app";
2
+ import {
3
+ onDocumentCreated,
4
+ onDocumentDeleted,
5
+ onDocumentUpdated,
6
+ onDocumentWritten,
7
+ } from "firebase-functions/v2/firestore";
8
+ import {onCall, onCallGenkit, HttpsError} from "firebase-functions/v2/https";
9
+ import {
10
+ setGlobalVariables,
11
+ verifyWriteLog,
12
+ autoIncrement,
13
+ updateIncludeFields,
14
+ validateRelations,
15
+ uniqueDelete,
16
+ fullTextSearch,
17
+ readApi,
18
+ validateFields,
19
+ writeApi,
20
+ validateUser,
21
+ getSchema,
22
+ removeRelations,
23
+ chatFlow,
24
+ writeEmbedding,
25
+ chatAuthPolicy,
26
+ deleteTrash,
27
+ searchResults,
28
+ customToken,
29
+ revokeMfa,
30
+ validateDenormalized,
31
+ getFiles,
32
+ deleteFolder,
33
+ messageNotifications,
34
+ deleteFiles,
35
+ sendSMSMessage,
36
+ getWriteLog,
37
+ } from "@stoker-platform/system-functions";
38
+ import {defineSecret} from "firebase-functions/params";
39
+ import {readFileSync} from "fs";
40
+ import {
41
+ getPathCollections,
42
+ } from "@stoker-platform/utils";
43
+ import {
44
+ CollectionSchema,
45
+ CollectionsSchema,
46
+ } from "@stoker-platform/types";
47
+ import globalConfig from "./system-custom/main.js";
48
+ import * as functions from "firebase-functions/v1";
49
+ import {genkit} from "genkit";
50
+ import {vertexAI} from "@genkit-ai/vertexai";
51
+ import {enableFirebaseTelemetry} from "@genkit-ai/firebase";
52
+ import {onSchedule} from "firebase-functions/v2/scheduler";
53
+ import {sendMail, sendMessage} from "@stoker-platform/node-client";
54
+
55
+ import * as dotenv from "dotenv";
56
+ dotenv.config();
57
+
58
+ initializeApp();
59
+ setGlobalVariables();
60
+
61
+ const projectDataFile = readFileSync("project-data.json", "utf8");
62
+ const projectData = JSON.parse(projectDataFile);
63
+ const timeZone: string = projectData.timezone;
64
+ const schema: CollectionsSchema = projectData.schema;
65
+ const consumeAppCheckToken =
66
+ process.env.FB_FUNCTIONS_CONSUME_APP_CHECK_TOKEN === "true";
67
+ const v1Region = process.env.FB_FUNCTIONS_V1_REGION ||
68
+ process.env.FB_FUNCTIONS_REGION;
69
+
70
+ const ai = genkit({
71
+ plugins: [vertexAI({
72
+ location: process.env.FB_AI_REGION || "us-central1",
73
+ })],
74
+ });
75
+
76
+ enableFirebaseTelemetry();
77
+
78
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
79
+ export const stoker: any = {};
80
+
81
+ // START CUSTOM FUNCTIONS
82
+
83
+ stoker["notifications"] = onDocumentCreated({
84
+ document: "tenants/{tenantId}/Inbox/{messageId}",
85
+ }, (event) => {
86
+ return messageNotifications(
87
+ event,
88
+ globalConfig,
89
+ );
90
+ });
91
+
92
+ // END CUSTOM FUNCTIONS
93
+
94
+ stoker["customtoken"] = onCall({
95
+ cors: true,
96
+ consumeAppCheckToken,
97
+ }, (request) => {
98
+ return customToken(request);
99
+ });
100
+
101
+ stoker["schema"] = onCall({
102
+ cors: true,
103
+ consumeAppCheckToken: false,
104
+ }, (request) => {
105
+ return getSchema(request);
106
+ });
107
+
108
+ stoker["readapi"] = onCall({
109
+ cors: true,
110
+ consumeAppCheckToken,
111
+ }, (request, response) => {
112
+ return readApi(request, response);
113
+ });
114
+
115
+ stoker["writeapi"] = onCall({
116
+ cors: true,
117
+ consumeAppCheckToken,
118
+ }, (request) => {
119
+ return writeApi(request);
120
+ });
121
+
122
+ if (process.env.STOKER_ALGOLIA_ID) {
123
+ const algoliaAdminKey = defineSecret("ALGOLIA_ADMIN_KEY");
124
+ stoker["search"] = onCall({
125
+ cors: true,
126
+ consumeAppCheckToken,
127
+ secrets: [algoliaAdminKey],
128
+ }, (request) => {
129
+ return searchResults(request, schema, algoliaAdminKey);
130
+ });
131
+ }
132
+
133
+ stoker["revokemfa"] = onCall({
134
+ cors: true,
135
+ consumeAppCheckToken,
136
+ }, (request) => {
137
+ return revokeMfa(request);
138
+ });
139
+
140
+ stoker["getfiles"] = onCall({
141
+ cors: true,
142
+ consumeAppCheckToken,
143
+ }, (request) => {
144
+ return getFiles(request, schema);
145
+ });
146
+
147
+ stoker["deletefolder"] = onCall({
148
+ cors: true,
149
+ consumeAppCheckToken,
150
+ }, (request) => {
151
+ return deleteFolder(request, schema);
152
+ });
153
+
154
+ stoker["writelog"] = onCall({
155
+ cors: true,
156
+ consumeAppCheckToken,
157
+ }, (request) => {
158
+ return getWriteLog(request, schema);
159
+ });
160
+
161
+ Object.values(schema.collections).forEach((collectionSchema) => {
162
+ const {labels, access, ai: aiConfig, softDelete} = collectionSchema;
163
+ const {serverWriteOnly} = access;
164
+ const collectionNameLower = labels.collection.toLowerCase();
165
+ const path = getPathCollections(collectionSchema, schema);
166
+ const document = path.map((collection: CollectionSchema) =>
167
+ // eslint-disable-next-line max-len
168
+ `tenants/{tenantId}/${collection.labels.collection}/{${collection.labels.record}Id}`)
169
+ .join("/");
170
+
171
+ // START CUSTOM COLLECTION LEVEL FUNCTIONS
172
+
173
+ // END CUSTOM COLLECTION LEVEL FUNCTIONS
174
+
175
+ const hasSkipValidationFields = collectionSchema.fields
176
+ .some((field) =>
177
+ "skipRulesValidation" in field && field.skipRulesValidation) ||
178
+ collectionSchema.skipRulesValidation;
179
+
180
+ if (hasSkipValidationFields && !serverWriteOnly) {
181
+ stoker[`validatefields${collectionNameLower}`] =
182
+ onDocumentWritten({
183
+ document,
184
+ retry: true,
185
+ }, (event) => {
186
+ return validateFields(
187
+ event,
188
+ collectionSchema,
189
+ globalConfig,
190
+ schema
191
+ );
192
+ });
193
+ }
194
+
195
+ if (collectionSchema.enableWriteLog) {
196
+ stoker[`verifywritelog${collectionNameLower}`] =
197
+ onDocumentWritten({
198
+ document,
199
+ retry: true,
200
+ }, (event) => {
201
+ return verifyWriteLog(event, collectionSchema);
202
+ });
203
+ }
204
+
205
+ if (
206
+ collectionSchema.fullTextSearch &&
207
+ process.env.STOKER_ALGOLIA_ID &&
208
+ !schema.config.roles.every((role) =>
209
+ collectionSchema.preloadCache?.roles.includes(role) ||
210
+ collectionSchema.access.serverReadOnly?.includes(role)
211
+ )) {
212
+ const algoliaAdminKey = defineSecret("ALGOLIA_ADMIN_KEY");
213
+ stoker[`fulltextsearch${collectionNameLower}`] =
214
+ onDocumentWritten({
215
+ document,
216
+ retry: true,
217
+ secrets: [algoliaAdminKey],
218
+ }, (event) => {
219
+ return fullTextSearch(
220
+ event,
221
+ collectionSchema,
222
+ algoliaAdminKey
223
+ );
224
+ });
225
+ }
226
+
227
+ const hasAutoIncrementFields = collectionSchema.fields
228
+ .some((field) => "autoIncrement" in field && field.autoIncrement);
229
+
230
+ if (hasAutoIncrementFields) {
231
+ stoker[`autoincrement${collectionNameLower}`] =
232
+ onDocumentWritten({
233
+ document,
234
+ retry: true,
235
+ }, (event) => {
236
+ return autoIncrement(event, collectionSchema, schema);
237
+ });
238
+ }
239
+
240
+ stoker[`validatedenormalized${collectionNameLower}`] =
241
+ onDocumentWritten({
242
+ document,
243
+ retry: true,
244
+ }, (event) => {
245
+ return validateDenormalized(event, collectionSchema, schema);
246
+ });
247
+
248
+ const hasDependentIncludeFields = Object.values(schema.collections)
249
+ .some((collection) => collection.fields
250
+ .some((field) => "collection" in field &&
251
+ field.collection === labels.collection &&
252
+ field.includeFields));
253
+
254
+ if (hasDependentIncludeFields) {
255
+ stoker[`includefields${collectionNameLower}`] =
256
+ onDocumentUpdated({
257
+ document,
258
+ retry: true,
259
+ }, (event) => {
260
+ return updateIncludeFields(event, collectionSchema, schema);
261
+ });
262
+ }
263
+
264
+ const hasRelationFields = collectionSchema.fields
265
+ .some((field) => "collection" in field && field.collection);
266
+
267
+ if (hasRelationFields) {
268
+ stoker[`validaterelations${
269
+ collectionNameLower
270
+ }`] =
271
+ onDocumentWritten({
272
+ document,
273
+ retry: true,
274
+ }, (event) => {
275
+ return validateRelations(
276
+ event,
277
+ collectionSchema,
278
+ schema,
279
+ );
280
+ });
281
+ }
282
+
283
+ const hasDependentNonTwoWayRelations = Object.values(schema.collections)
284
+ .some((collection) => collection.fields
285
+ .some((field) => "collection" in field &&
286
+ field.collection === labels.collection &&
287
+ !("twoWay" in field && field.twoWay)));
288
+
289
+ if (hasDependentNonTwoWayRelations) {
290
+ stoker[`removerelations${
291
+ collectionNameLower
292
+ }`] =
293
+ onDocumentDeleted({
294
+ document,
295
+ retry: true,
296
+ }, (event) => {
297
+ return removeRelations(
298
+ event,
299
+ collectionSchema,
300
+ schema,
301
+ );
302
+ });
303
+ }
304
+
305
+ const hasUniqueFields = collectionSchema.fields
306
+ .some((field) => "unique" in field && field.unique);
307
+
308
+ if (hasUniqueFields) {
309
+ stoker[`uniquedelete${collectionNameLower}`] =
310
+ onDocumentWritten({
311
+ document,
312
+ retry: true,
313
+ }, (event) => {
314
+ return uniqueDelete(
315
+ event,
316
+ collectionSchema,
317
+ );
318
+ });
319
+ }
320
+
321
+ stoker[`deletefiles${collectionNameLower}`] =
322
+ onDocumentDeleted({
323
+ document,
324
+ retry: true,
325
+ }, (event) => {
326
+ return deleteFiles(
327
+ event,
328
+ collectionSchema,
329
+ );
330
+ });
331
+
332
+ if (aiConfig?.embedding) {
333
+ stoker[`embedding${collectionNameLower}`] =
334
+ onDocumentWritten({
335
+ document,
336
+ retry: true,
337
+ }, (event) => {
338
+ return writeEmbedding(
339
+ event,
340
+ collectionSchema,
341
+ schema,
342
+ );
343
+ });
344
+ }
345
+
346
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
347
+ const webAppConfig = JSON.parse(process.env.STOKER_FB_WEB_APP_CONFIG!);
348
+ const projectNumber = webAppConfig.messagingSenderId;
349
+ if (aiConfig?.chat) {
350
+ stoker[`chat${collectionNameLower}`] =
351
+ onCallGenkit({
352
+ cors: true,
353
+ consumeAppCheckToken: false,
354
+ // eslint-disable-next-line max-len
355
+ serviceAccount: `${projectNumber}-compute@developer.gserviceaccount.com`,
356
+ authPolicy: (auth) => chatAuthPolicy(auth, collectionSchema),
357
+ }, chatFlow(collectionSchema, schema, ai));
358
+ }
359
+
360
+ if (softDelete) {
361
+ stoker[`deletetrash${collectionNameLower}`] = onSchedule({
362
+ region: process.env.FB_FUNCTIONS_V1_REGION ||
363
+ process.env.FB_FUNCTIONS_REGION,
364
+ schedule: "every day 00:00",
365
+ timeZone,
366
+ retryCount: 3,
367
+ }, () => {
368
+ return deleteTrash(
369
+ collectionSchema,
370
+ );
371
+ });
372
+ }
373
+ });
374
+
375
+ const enforceAppCheck = process.env.STOKER_FB_ENABLE_APP_CHECK === "true";
376
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
377
+ const timeoutSeconds = parseInt(process.env.FB_FUNCTIONS_TIMEOUT!);
378
+
379
+ stoker["validateuser"] =
380
+ functions.runWith({
381
+ timeoutSeconds,
382
+ enforceAppCheck,
383
+ failurePolicy: true,
384
+ memory: "1GB",
385
+ minInstances: parseInt(
386
+ process.env.FB_FUNCTIONS_MIN_INSTANCES || "0",
387
+ ),
388
+ maxInstances: parseInt(
389
+ process.env.FB_FUNCTIONS_MAX_INSTANCES || "5",
390
+ ),
391
+ }).region(v1Region || "us-west1").auth.user().onCreate((user) => {
392
+ return validateUser(user);
393
+ });
394
+
395
+ if (process.env.STOKER_SMS_ENABLED === "true") {
396
+ const twilioAccountSid = defineSecret("TWILIO_ACCOUNT_SID");
397
+ const twilioAuthToken = defineSecret("TWILIO_AUTH_TOKEN");
398
+ const twilioPhoneNumber = defineSecret("TWILIO_PHONE_NUMBER");
399
+ stoker["sendmessage"] = onDocumentCreated({
400
+ document: "system_messages/{messageId}",
401
+ retry: true,
402
+ secrets: [twilioAccountSid, twilioAuthToken, twilioPhoneNumber],
403
+ }, (event) => {
404
+ return sendSMSMessage(
405
+ event,
406
+ twilioAccountSid,
407
+ twilioAuthToken,
408
+ twilioPhoneNumber,
409
+ );
410
+ });
411
+ }
412
+
413
+ stoker["adminemail"] = onCall({
414
+ cors: true,
415
+ consumeAppCheckToken,
416
+ }, (request) => {
417
+ const user = request.auth?.uid;
418
+ const token = request.auth?.token;
419
+ if (!(token?.tenant && token?.collection && token?.role && token?.doc)) {
420
+ throw new HttpsError(
421
+ "permission-denied",
422
+ "User does not have permission to access this database",
423
+ );
424
+ }
425
+ if (!user) {
426
+ throw new HttpsError("unauthenticated", "User is not authenticated");
427
+ }
428
+ if (!process.env.ADMIN_EMAIL) return;
429
+ return sendMail(
430
+ process.env.ADMIN_EMAIL,
431
+ request.data.subject,
432
+ request.data.text,
433
+ request.data.html,
434
+ request.data.cc,
435
+ request.data.bcc,
436
+ request.data.replyTo
437
+ );
438
+ });
439
+
440
+ stoker["adminsms"] = onCall({
441
+ cors: true,
442
+ consumeAppCheckToken,
443
+ }, (request) => {
444
+ const user = request.auth?.uid;
445
+ const token = request.auth?.token;
446
+ if (!(token?.tenant && token?.collection && token?.role && token?.doc)) {
447
+ throw new HttpsError(
448
+ "permission-denied",
449
+ "User does not have permission to access this database",
450
+ );
451
+ }
452
+ if (!user) {
453
+ throw new HttpsError("unauthenticated", "User is not authenticated");
454
+ }
455
+ if (!process.env.ADMIN_PHONE) return;
456
+ return sendMessage(process.env.ADMIN_PHONE, request.data.body);
457
+ });
@@ -0,0 +1,3 @@
1
+ {
2
+ "include": [".eslintrc.cjs"]
3
+ }
@@ -0,0 +1,17 @@
1
+ {
2
+ "compilerOptions": {
3
+ "module": "ESNext",
4
+ "noImplicitReturns": true,
5
+ "noUnusedLocals": true,
6
+ "outDir": "lib",
7
+ "sourceMap": true,
8
+ "strict": true,
9
+ "target": "ESNext",
10
+ "moduleResolution": "Node",
11
+
12
+ "allowSyntheticDefaultImports": true,
13
+ "jsx": "react-jsx"
14
+ },
15
+ "compileOnSave": true,
16
+ "include": ["src"]
17
+ }
Binary file
Binary file
@@ -0,0 +1,25 @@
1
+ #!/usr/bin/env node
2
+ import { initializeStoker } from "@stoker-platform/node-client"
3
+ import { join } from "path"
4
+ import { existsSync } from "fs"
5
+
6
+ import dotenv from "dotenv"
7
+ const projectEnvFile = join(process.cwd(), ".env", `.env.project.${process.env.GCP_PROJECT}`)
8
+ // eslint-disable-next-line security/detect-non-literal-fs-filename
9
+ if (existsSync(projectEnvFile)) {
10
+ dotenv.config({ path: projectEnvFile, quiet: true })
11
+ } else {
12
+ dotenv.config({ path: ".env/.env", quiet: true })
13
+ }
14
+ dotenv.config({ path: `.env/.env.${process.env.GCP_PROJECT}`, quiet: true })
15
+
16
+ const tenantId = ""
17
+
18
+ await initializeStoker(
19
+ "production",
20
+ tenantId,
21
+ join(process.cwd(), "lib", "main.js"),
22
+ join(process.cwd(), "lib", "collections"),
23
+ )
24
+
25
+ process.exit()
@@ -0,0 +1,53 @@
1
+ {
2
+ "name": "stoker",
3
+ "private": true,
4
+ "version": "0.0.1",
5
+ "type": "module",
6
+ "license": "UNLICENSED",
7
+ "scripts": {
8
+ "clean:lib": "rm -rf lib",
9
+ "clean:functions": "rm -rf functions/lib",
10
+ "dev": "concurrently \"nodemon --quiet --watch src --ext ts,tsx,js,json --exec 'npm run clean:lib && tsc && cp -r src/. web-app/src/assets/system-custom && cp lib/main.js web-app/src/assets/system-custom/main.js'\" \"npm --prefix functions run build:watch\"",
11
+ "web:dev": "cd web-app && npm run dev",
12
+ "prestart": "npm run build && npx stoker build-web-app && rm -rf web-app && mkdir web-app && cp -r node_modules/@stoker-platform/web-app/. web-app/ && stoker apply",
13
+ "start": "concurrently \"npm run dev\" \"npm run web:dev\" \"stoker start\"",
14
+ "format": "prettier . --write",
15
+ "lint": "tsc --noEmit && eslint . && npm --prefix functions run lint",
16
+ "prebuild": "npm run clean:lib && npm run clean:functions",
17
+ "build": "tsc && node bin/build.js && node bin/shim.js && npm --prefix functions run build",
18
+ "test": "vitest --run",
19
+ "admin-emulators": "export FIREBASE_AUTH_EMULATOR_HOST='127.0.0.1:9099' && export FIREBASE_DATABASE_EMULATOR_HOST='127.0.0.1:9000' && export FIRESTORE_EMULATOR_HOST='127.0.0.1:8080' && export FIREBASE_STORAGE_EMULATOR_HOST='127.0.0.1:9199'"
20
+ },
21
+ "dependencies": {
22
+ "@stoker-platform/cli": "latest",
23
+ "@stoker-platform/node-client": "latest",
24
+ "@stoker-platform/types": "latest",
25
+ "@stoker-platform/utils": "latest",
26
+ "@stoker-platform/web-app": "latest",
27
+ "@stoker-platform/web-client": "latest",
28
+ "firebase-admin": "latest"
29
+ },
30
+ "devDependencies": {
31
+ "@typescript-eslint/eslint-plugin": "latest",
32
+ "@typescript-eslint/parser": "latest",
33
+ "@vite-pwa/assets-generator": "latest",
34
+ "@vitest/browser": "latest",
35
+ "@vitest/ui": "latest",
36
+ "autoprefixer": "latest",
37
+ "concurrently": "latest",
38
+ "eslint": "^8.57.1",
39
+ "eslint-config-prettier": "latest",
40
+ "eslint-plugin-import": "latest",
41
+ "eslint-plugin-security": "latest",
42
+ "postcss": "latest",
43
+ "prettier": "3.7.4",
44
+ "tailwindcss": "^3.4.17",
45
+ "typescript": "latest",
46
+ "vite-plugin-pwa": "latest",
47
+ "vitest": "latest"
48
+ },
49
+ "engines": {
50
+ "node": ">=22",
51
+ "npm": "11"
52
+ }
53
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "version": 1,
3
+ "projects": [],
4
+ "deleted_projects": []
5
+ }
@@ -0,0 +1 @@
1
+ {}