@bloomneo/appkit 1.5.1 → 1.5.2

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 (111) hide show
  1. package/AGENTS.md +195 -0
  2. package/CHANGELOG.md +253 -0
  3. package/README.md +147 -799
  4. package/bin/commands/generate.js +7 -7
  5. package/cookbook/README.md +26 -0
  6. package/cookbook/api-key-service.ts +106 -0
  7. package/cookbook/auth-protected-crud.ts +112 -0
  8. package/cookbook/file-upload-pipeline.ts +113 -0
  9. package/cookbook/multi-tenant-saas.ts +87 -0
  10. package/cookbook/real-time-chat.ts +121 -0
  11. package/dist/auth/auth.d.ts +21 -4
  12. package/dist/auth/auth.d.ts.map +1 -1
  13. package/dist/auth/auth.js +56 -44
  14. package/dist/auth/auth.js.map +1 -1
  15. package/dist/auth/defaults.d.ts +1 -1
  16. package/dist/auth/defaults.js +35 -35
  17. package/dist/cache/cache.d.ts +29 -6
  18. package/dist/cache/cache.d.ts.map +1 -1
  19. package/dist/cache/cache.js +72 -44
  20. package/dist/cache/cache.js.map +1 -1
  21. package/dist/cache/defaults.js +25 -25
  22. package/dist/cache/index.d.ts +19 -10
  23. package/dist/cache/index.d.ts.map +1 -1
  24. package/dist/cache/index.js +21 -18
  25. package/dist/cache/index.js.map +1 -1
  26. package/dist/config/defaults.d.ts +1 -1
  27. package/dist/config/defaults.js +8 -8
  28. package/dist/config/index.d.ts +3 -3
  29. package/dist/config/index.js +4 -4
  30. package/dist/database/adapters/mongoose.js +2 -2
  31. package/dist/database/adapters/prisma.js +2 -2
  32. package/dist/database/defaults.d.ts +1 -1
  33. package/dist/database/defaults.js +4 -4
  34. package/dist/database/index.js +2 -2
  35. package/dist/database/index.js.map +1 -1
  36. package/dist/email/defaults.js +20 -20
  37. package/dist/error/defaults.d.ts +1 -1
  38. package/dist/error/defaults.js +12 -12
  39. package/dist/error/error.d.ts +12 -0
  40. package/dist/error/error.d.ts.map +1 -1
  41. package/dist/error/error.js +19 -0
  42. package/dist/error/error.js.map +1 -1
  43. package/dist/error/index.d.ts +14 -3
  44. package/dist/error/index.d.ts.map +1 -1
  45. package/dist/error/index.js +14 -3
  46. package/dist/error/index.js.map +1 -1
  47. package/dist/event/defaults.js +30 -30
  48. package/dist/logger/defaults.d.ts +1 -1
  49. package/dist/logger/defaults.js +40 -40
  50. package/dist/logger/index.d.ts +1 -0
  51. package/dist/logger/index.d.ts.map +1 -1
  52. package/dist/logger/index.js.map +1 -1
  53. package/dist/logger/logger.d.ts +8 -0
  54. package/dist/logger/logger.d.ts.map +1 -1
  55. package/dist/logger/logger.js +13 -3
  56. package/dist/logger/logger.js.map +1 -1
  57. package/dist/logger/transports/console.js +1 -1
  58. package/dist/logger/transports/http.d.ts +1 -1
  59. package/dist/logger/transports/http.js +1 -1
  60. package/dist/logger/transports/webhook.d.ts +1 -1
  61. package/dist/logger/transports/webhook.js +1 -1
  62. package/dist/queue/defaults.d.ts +2 -2
  63. package/dist/queue/defaults.js +38 -38
  64. package/dist/security/defaults.d.ts +1 -1
  65. package/dist/security/defaults.js +29 -29
  66. package/dist/security/index.d.ts +1 -1
  67. package/dist/security/index.js +3 -3
  68. package/dist/security/security.d.ts +1 -1
  69. package/dist/security/security.js +4 -4
  70. package/dist/storage/defaults.js +19 -19
  71. package/dist/util/defaults.d.ts +1 -1
  72. package/dist/util/defaults.js +34 -34
  73. package/dist/util/env.d.ts +35 -0
  74. package/dist/util/env.d.ts.map +1 -0
  75. package/dist/util/env.js +50 -0
  76. package/dist/util/env.js.map +1 -0
  77. package/dist/util/errors.d.ts +52 -0
  78. package/dist/util/errors.d.ts.map +1 -0
  79. package/dist/util/errors.js +82 -0
  80. package/dist/util/errors.js.map +1 -0
  81. package/examples/.env.example +80 -0
  82. package/examples/README.md +16 -0
  83. package/examples/auth.ts +228 -0
  84. package/examples/cache.ts +36 -0
  85. package/examples/config.ts +45 -0
  86. package/examples/database.ts +69 -0
  87. package/examples/email.ts +53 -0
  88. package/examples/error.ts +50 -0
  89. package/examples/event.ts +42 -0
  90. package/examples/logger.ts +41 -0
  91. package/examples/queue.ts +58 -0
  92. package/examples/security.ts +46 -0
  93. package/examples/storage.ts +44 -0
  94. package/examples/util.ts +47 -0
  95. package/llms.txt +591 -0
  96. package/package.json +19 -10
  97. package/src/auth/README.md +850 -0
  98. package/src/cache/README.md +756 -0
  99. package/src/config/README.md +604 -0
  100. package/src/database/README.md +818 -0
  101. package/src/email/README.md +759 -0
  102. package/src/error/README.md +660 -0
  103. package/src/event/README.md +729 -0
  104. package/src/logger/README.md +435 -0
  105. package/src/queue/README.md +851 -0
  106. package/src/security/README.md +612 -0
  107. package/src/storage/README.md +1008 -0
  108. package/src/util/README.md +955 -0
  109. package/bin/templates/backend/docs/APPKIT_CLI.md +0 -507
  110. package/bin/templates/backend/docs/APPKIT_COMMENTS_GUIDELINES.md +0 -61
  111. package/bin/templates/backend/docs/APPKIT_LLM_GUIDE.md +0 -2539
@@ -8,7 +8,7 @@
8
8
  * @llm-rule NOTE: Called once at startup, cached globally for performance like other modules
9
9
  */
10
10
  /**
11
- * Gets smart defaults using VOILA_UTIL_* environment variables
11
+ * Gets smart defaults using BLOOM_UTIL_* environment variables
12
12
  * @llm-rule WHEN: App startup to get production-ready utility configuration
13
13
  * @llm-rule AVOID: Calling repeatedly - expensive validation, cache the result
14
14
  * @llm-rule NOTE: Called once at startup, cached globally for performance
@@ -23,36 +23,36 @@ export function getSmartDefaults() {
23
23
  version: process.env.npm_package_version || '1.0.0',
24
24
  // Cache configuration with direct environment access
25
25
  cache: {
26
- enabled: process.env.VOILA_UTIL_CACHE !== 'false' && !isTest,
27
- maxSize: parseInt(process.env.VOILA_UTIL_CACHE_SIZE || '1000'),
28
- ttl: parseInt(process.env.VOILA_UTIL_CACHE_TTL || '300000'), // 5 minutes
26
+ enabled: process.env.BLOOM_UTIL_CACHE !== 'false' && !isTest,
27
+ maxSize: parseInt(process.env.BLOOM_UTIL_CACHE_SIZE || '1000'),
28
+ ttl: parseInt(process.env.BLOOM_UTIL_CACHE_TTL || '300000'), // 5 minutes
29
29
  },
30
30
  // Performance optimization settings
31
31
  performance: {
32
- enabled: process.env.VOILA_UTIL_PERFORMANCE !== 'false',
33
- memoization: process.env.VOILA_UTIL_MEMOIZATION !== 'false' && !isTest,
34
- largeArrayThreshold: parseInt(process.env.VOILA_UTIL_ARRAY_THRESHOLD || '10000'),
35
- chunkSizeLimit: parseInt(process.env.VOILA_UTIL_CHUNK_LIMIT || '100000'),
32
+ enabled: process.env.BLOOM_UTIL_PERFORMANCE !== 'false',
33
+ memoization: process.env.BLOOM_UTIL_MEMOIZATION !== 'false' && !isTest,
34
+ largeArrayThreshold: parseInt(process.env.BLOOM_UTIL_ARRAY_THRESHOLD || '10000'),
35
+ chunkSizeLimit: parseInt(process.env.BLOOM_UTIL_CHUNK_LIMIT || '100000'),
36
36
  },
37
37
  // Debug configuration - enabled in development
38
38
  debug: {
39
- enabled: process.env.VOILA_UTIL_DEBUG === 'true' || isDevelopment,
40
- logOperations: process.env.VOILA_UTIL_LOG_OPS === 'true' || isDevelopment,
41
- trackPerformance: process.env.VOILA_UTIL_TRACK_PERF === 'true' || isDevelopment,
39
+ enabled: process.env.BLOOM_UTIL_DEBUG === 'true' || isDevelopment,
40
+ logOperations: process.env.BLOOM_UTIL_LOG_OPS === 'true' || isDevelopment,
41
+ trackPerformance: process.env.BLOOM_UTIL_TRACK_PERF === 'true' || isDevelopment,
42
42
  },
43
43
  // Slugify configuration with locale support
44
44
  slugify: {
45
- lowercase: process.env.VOILA_UTIL_SLUGIFY_LOWERCASE !== 'false',
46
- strict: process.env.VOILA_UTIL_SLUGIFY_STRICT === 'true',
47
- locale: process.env.VOILA_UTIL_LOCALE || 'en',
48
- replacement: process.env.VOILA_UTIL_SLUGIFY_REPLACEMENT || '-',
45
+ lowercase: process.env.BLOOM_UTIL_SLUGIFY_LOWERCASE !== 'false',
46
+ strict: process.env.BLOOM_UTIL_SLUGIFY_STRICT === 'true',
47
+ locale: process.env.BLOOM_UTIL_LOCALE || 'en',
48
+ replacement: process.env.BLOOM_UTIL_SLUGIFY_REPLACEMENT || '-',
49
49
  },
50
50
  // Format configuration for locale-aware formatting
51
51
  format: {
52
- locale: process.env.VOILA_UTIL_LOCALE || 'en-US',
53
- currency: process.env.VOILA_UTIL_CURRENCY || 'USD',
54
- dateFormat: process.env.VOILA_UTIL_DATE_FORMAT || 'YYYY-MM-DD',
55
- numberPrecision: parseInt(process.env.VOILA_UTIL_NUMBER_PRECISION || '2'),
52
+ locale: process.env.BLOOM_UTIL_LOCALE || 'en-US',
53
+ currency: process.env.BLOOM_UTIL_CURRENCY || 'USD',
54
+ dateFormat: process.env.BLOOM_UTIL_DATE_FORMAT || 'YYYY-MM-DD',
55
+ numberPrecision: parseInt(process.env.BLOOM_UTIL_NUMBER_PRECISION || '2'),
56
56
  },
57
57
  // Environment information
58
58
  environment: {
@@ -72,69 +72,69 @@ export function getSmartDefaults() {
72
72
  function validateEnvironment() {
73
73
  const nodeEnv = process.env.NODE_ENV || 'development';
74
74
  // Validate cache configuration
75
- const cacheSize = process.env.VOILA_UTIL_CACHE_SIZE;
75
+ const cacheSize = process.env.BLOOM_UTIL_CACHE_SIZE;
76
76
  if (cacheSize) {
77
77
  const cacheSizeNum = parseInt(cacheSize);
78
78
  if (isNaN(cacheSizeNum) || cacheSizeNum <= 0) {
79
- throw new Error(`Invalid VOILA_UTIL_CACHE_SIZE: "${cacheSize}". Must be a positive number.`);
79
+ throw new Error(`Invalid BLOOM_UTIL_CACHE_SIZE: "${cacheSize}". Must be a positive number.`);
80
80
  }
81
81
  if (cacheSizeNum > 100000) {
82
82
  console.warn(`[Bloomneo AppKit] Large cache size: ${cacheSizeNum}. This may impact memory usage.`);
83
83
  }
84
84
  }
85
85
  // Validate cache TTL
86
- const cacheTTL = process.env.VOILA_UTIL_CACHE_TTL;
86
+ const cacheTTL = process.env.BLOOM_UTIL_CACHE_TTL;
87
87
  if (cacheTTL) {
88
88
  const cacheTTLNum = parseInt(cacheTTL);
89
89
  if (isNaN(cacheTTLNum) || cacheTTLNum <= 0) {
90
- throw new Error(`Invalid VOILA_UTIL_CACHE_TTL: "${cacheTTL}". Must be a positive number (milliseconds).`);
90
+ throw new Error(`Invalid BLOOM_UTIL_CACHE_TTL: "${cacheTTL}". Must be a positive number (milliseconds).`);
91
91
  }
92
92
  }
93
93
  // Validate array threshold
94
- const arrayThreshold = process.env.VOILA_UTIL_ARRAY_THRESHOLD;
94
+ const arrayThreshold = process.env.BLOOM_UTIL_ARRAY_THRESHOLD;
95
95
  if (arrayThreshold) {
96
96
  const thresholdNum = parseInt(arrayThreshold);
97
97
  if (isNaN(thresholdNum) || thresholdNum <= 0) {
98
- throw new Error(`Invalid VOILA_UTIL_ARRAY_THRESHOLD: "${arrayThreshold}". Must be a positive number.`);
98
+ throw new Error(`Invalid BLOOM_UTIL_ARRAY_THRESHOLD: "${arrayThreshold}". Must be a positive number.`);
99
99
  }
100
100
  }
101
101
  // Validate chunk size limit
102
- const chunkLimit = process.env.VOILA_UTIL_CHUNK_LIMIT;
102
+ const chunkLimit = process.env.BLOOM_UTIL_CHUNK_LIMIT;
103
103
  if (chunkLimit) {
104
104
  const chunkLimitNum = parseInt(chunkLimit);
105
105
  if (isNaN(chunkLimitNum) || chunkLimitNum <= 0) {
106
- throw new Error(`Invalid VOILA_UTIL_CHUNK_LIMIT: "${chunkLimit}". Must be a positive number.`);
106
+ throw new Error(`Invalid BLOOM_UTIL_CHUNK_LIMIT: "${chunkLimit}". Must be a positive number.`);
107
107
  }
108
108
  }
109
109
  // Validate number precision
110
- const numberPrecision = process.env.VOILA_UTIL_NUMBER_PRECISION;
110
+ const numberPrecision = process.env.BLOOM_UTIL_NUMBER_PRECISION;
111
111
  if (numberPrecision) {
112
112
  const precisionNum = parseInt(numberPrecision);
113
113
  if (isNaN(precisionNum) || precisionNum < 0 || precisionNum > 20) {
114
- throw new Error(`Invalid VOILA_UTIL_NUMBER_PRECISION: "${numberPrecision}". Must be between 0 and 20.`);
114
+ throw new Error(`Invalid BLOOM_UTIL_NUMBER_PRECISION: "${numberPrecision}". Must be between 0 and 20.`);
115
115
  }
116
116
  }
117
117
  // Validate locale if provided
118
- const locale = process.env.VOILA_UTIL_LOCALE;
118
+ const locale = process.env.BLOOM_UTIL_LOCALE;
119
119
  if (locale && !isValidLocale(locale)) {
120
120
  console.warn(`[Bloomneo AppKit] Invalid locale: "${locale}". Using default 'en-US'.`);
121
121
  }
122
122
  // Validate currency if provided
123
- const currency = process.env.VOILA_UTIL_CURRENCY;
123
+ const currency = process.env.BLOOM_UTIL_CURRENCY;
124
124
  if (currency && !isValidCurrency(currency)) {
125
125
  console.warn(`[Bloomneo AppKit] Invalid currency: "${currency}". Using default 'USD'.`);
126
126
  }
127
127
  // Validate slugify replacement
128
- const replacement = process.env.VOILA_UTIL_SLUGIFY_REPLACEMENT;
128
+ const replacement = process.env.BLOOM_UTIL_SLUGIFY_REPLACEMENT;
129
129
  if (replacement && replacement.length > 5) {
130
130
  console.warn(`[Bloomneo AppKit] Long slugify replacement: "${replacement}". Consider using shorter replacement.`);
131
131
  }
132
132
  // Production-specific warnings
133
133
  if (nodeEnv === 'production') {
134
- if (process.env.VOILA_UTIL_DEBUG === 'true') {
134
+ if (process.env.BLOOM_UTIL_DEBUG === 'true') {
135
135
  console.warn('[Bloomneo AppKit] Debug mode enabled in production. This may impact performance.');
136
136
  }
137
- if (process.env.VOILA_UTIL_LOG_OPS === 'true') {
137
+ if (process.env.BLOOM_UTIL_LOG_OPS === 'true') {
138
138
  console.warn('[Bloomneo AppKit] Operation logging enabled in production. This may impact performance.');
139
139
  }
140
140
  }
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Environment variable reader for @bloomneo/appkit.
3
+ *
4
+ * Canonical prefix is `BLOOM_`. The legacy `VOILA_*` prefix from the original
5
+ * `@voilajsx/appkit` package was removed entirely in 1.5.2 — there is no
6
+ * backwards-compatibility, no fallback, no deprecation warning. Consumers
7
+ * upgrading must rename their env vars in one go.
8
+ *
9
+ * Internal use only. Consumers should call `configClass.get()` instead of
10
+ * touching env vars directly. This file exists so future code has one
11
+ * canonical helper for reading env vars, instead of scattering
12
+ * `process.env.BLOOM_*` calls across the codebase.
13
+ *
14
+ * @file src/util/env.ts
15
+ */
16
+ /**
17
+ * Read an env var by its bare name (no prefix).
18
+ *
19
+ * @example
20
+ * env('AUTH_SECRET') // → process.env.BLOOM_AUTH_SECRET
21
+ * env('DB_TENANT') // → process.env.BLOOM_DB_TENANT
22
+ * env('FOO', 'default-val') // → returns 'default-val' if BLOOM_FOO unset
23
+ */
24
+ export declare function env(name: string, fallback?: string): string | undefined;
25
+ /**
26
+ * Read a numeric env var. Returns the fallback if the var is unset or
27
+ * the value can't be parsed as a finite number.
28
+ */
29
+ export declare function envNumber(name: string, fallback: number): number;
30
+ /**
31
+ * Read a boolean env var. Truthy: 'true' / '1' / 'yes' / 'on' (case-insensitive).
32
+ * Returns the fallback if the var is unset.
33
+ */
34
+ export declare function envBool(name: string, fallback: boolean): boolean;
35
+ //# sourceMappingURL=env.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"env.d.ts","sourceRoot":"","sources":["../../src/util/env.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH;;;;;;;GAOG;AACH,wBAAgB,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAGvE;AAED;;;GAGG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAKhE;AAED;;;GAGG;AACH,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,GAAG,OAAO,CAKhE"}
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Environment variable reader for @bloomneo/appkit.
3
+ *
4
+ * Canonical prefix is `BLOOM_`. The legacy `VOILA_*` prefix from the original
5
+ * `@voilajsx/appkit` package was removed entirely in 1.5.2 — there is no
6
+ * backwards-compatibility, no fallback, no deprecation warning. Consumers
7
+ * upgrading must rename their env vars in one go.
8
+ *
9
+ * Internal use only. Consumers should call `configClass.get()` instead of
10
+ * touching env vars directly. This file exists so future code has one
11
+ * canonical helper for reading env vars, instead of scattering
12
+ * `process.env.BLOOM_*` calls across the codebase.
13
+ *
14
+ * @file src/util/env.ts
15
+ */
16
+ /**
17
+ * Read an env var by its bare name (no prefix).
18
+ *
19
+ * @example
20
+ * env('AUTH_SECRET') // → process.env.BLOOM_AUTH_SECRET
21
+ * env('DB_TENANT') // → process.env.BLOOM_DB_TENANT
22
+ * env('FOO', 'default-val') // → returns 'default-val' if BLOOM_FOO unset
23
+ */
24
+ export function env(name, fallback) {
25
+ const value = process.env[`BLOOM_${name}`];
26
+ return value !== undefined ? value : fallback;
27
+ }
28
+ /**
29
+ * Read a numeric env var. Returns the fallback if the var is unset or
30
+ * the value can't be parsed as a finite number.
31
+ */
32
+ export function envNumber(name, fallback) {
33
+ const raw = env(name);
34
+ if (raw === undefined)
35
+ return fallback;
36
+ const parsed = Number(raw);
37
+ return Number.isFinite(parsed) ? parsed : fallback;
38
+ }
39
+ /**
40
+ * Read a boolean env var. Truthy: 'true' / '1' / 'yes' / 'on' (case-insensitive).
41
+ * Returns the fallback if the var is unset.
42
+ */
43
+ export function envBool(name, fallback) {
44
+ const raw = env(name);
45
+ if (raw === undefined)
46
+ return fallback;
47
+ const v = raw.toLowerCase();
48
+ return v === 'true' || v === '1' || v === 'yes' || v === 'on';
49
+ }
50
+ //# sourceMappingURL=env.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"env.js","sourceRoot":"","sources":["../../src/util/env.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH;;;;;;;GAOG;AACH,MAAM,UAAU,GAAG,CAAC,IAAY,EAAE,QAAiB;IACjD,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,SAAS,IAAI,EAAE,CAAC,CAAC;IAC3C,OAAO,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC;AAChD,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,SAAS,CAAC,IAAY,EAAE,QAAgB;IACtD,MAAM,GAAG,GAAG,GAAG,CAAC,IAAI,CAAC,CAAC;IACtB,IAAI,GAAG,KAAK,SAAS;QAAE,OAAO,QAAQ,CAAC;IACvC,MAAM,MAAM,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;IAC3B,OAAO,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC;AACrD,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,OAAO,CAAC,IAAY,EAAE,QAAiB;IACrD,MAAM,GAAG,GAAG,GAAG,CAAC,IAAI,CAAC,CAAC;IACtB,IAAI,GAAG,KAAK,SAAS;QAAE,OAAO,QAAQ,CAAC;IACvC,MAAM,CAAC,GAAG,GAAG,CAAC,WAAW,EAAE,CAAC;IAC5B,OAAO,CAAC,KAAK,MAAM,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,KAAK,IAAI,CAAC,KAAK,IAAI,CAAC;AAChE,CAAC"}
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Educational runtime errors for @bloomneo/appkit.
3
+ *
4
+ * Modules throw these (instead of plain TypeError or generic Error) so that
5
+ * humans AND AI coding agents reading the error message get an actionable
6
+ * fix-it-now message that names the missing thing AND points at the canonical
7
+ * pattern in AGENTS.md or the relevant example file.
8
+ *
9
+ * Format is intentionally consistent across all 12 modules:
10
+ *
11
+ * [@bloomneo/appkit] <Module> requires <thing>. <reason>.
12
+ * See: <docs URL or example file>
13
+ *
14
+ * Agents that read errors and self-correct will fetch the link and recover
15
+ * on the next iteration. Without this, agents get cryptic "X is undefined"
16
+ * messages and have to guess.
17
+ *
18
+ * @file src/util/errors.ts
19
+ */
20
+ export declare class AppKitError extends Error {
21
+ readonly module: string;
22
+ readonly docsUrl: string;
23
+ constructor(module: string, message: string, slug?: string);
24
+ }
25
+ /**
26
+ * Throw if a required env var is missing. Use at module init / first .get()
27
+ * call so the error is loud and immediate, not deferred to the first request.
28
+ *
29
+ * @example
30
+ * requireEnv('Auth', 'BLOOM_AUTH_SECRET',
31
+ * 'Set this to a 32+ character random string. See examples/.env.example.');
32
+ */
33
+ export declare function requireEnv(module: string, varName: string, hint?: string): string;
34
+ /**
35
+ * Throw if `value` is missing or wrongly typed. Use at the top of public
36
+ * methods that depend on caller-provided data.
37
+ *
38
+ * @example
39
+ * const auth = authClass.get();
40
+ * auth.signToken = (payload) => {
41
+ * requireProp('Auth', 'signToken.payload', payload);
42
+ * // ...
43
+ * };
44
+ */
45
+ export declare function requireProp<T>(module: string, prop: string, value: T | undefined | null, hint?: string): T;
46
+ /**
47
+ * Lighter-weight version that only warns in development. Use for nice-to-have
48
+ * conventions ("you should pass a 32-char secret, not a 16-char one") that
49
+ * shouldn't crash a production app on startup.
50
+ */
51
+ export declare function warnInDev(module: string, message: string, slug?: string): void;
52
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/util/errors.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAIH,qBAAa,WAAY,SAAQ,KAAK;IACpC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;gBAEb,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM;CAO3D;AAED;;;;;;;GAOG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CASjF;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAC3B,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,CAAC,GAAG,SAAS,GAAG,IAAI,EAC3B,IAAI,CAAC,EAAE,MAAM,GACZ,CAAC,CAQH;AAED;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAK9E"}
@@ -0,0 +1,82 @@
1
+ /**
2
+ * Educational runtime errors for @bloomneo/appkit.
3
+ *
4
+ * Modules throw these (instead of plain TypeError or generic Error) so that
5
+ * humans AND AI coding agents reading the error message get an actionable
6
+ * fix-it-now message that names the missing thing AND points at the canonical
7
+ * pattern in AGENTS.md or the relevant example file.
8
+ *
9
+ * Format is intentionally consistent across all 12 modules:
10
+ *
11
+ * [@bloomneo/appkit] <Module> requires <thing>. <reason>.
12
+ * See: <docs URL or example file>
13
+ *
14
+ * Agents that read errors and self-correct will fetch the link and recover
15
+ * on the next iteration. Without this, agents get cryptic "X is undefined"
16
+ * messages and have to guess.
17
+ *
18
+ * @file src/util/errors.ts
19
+ */
20
+ const DOCS_BASE = 'https://github.com/bloomneo/appkit/blob/main/AGENTS.md';
21
+ export class AppKitError extends Error {
22
+ module;
23
+ docsUrl;
24
+ constructor(module, message, slug) {
25
+ const url = slug ? `${DOCS_BASE}#${slug}` : DOCS_BASE;
26
+ super(`[@bloomneo/appkit] ${module}: ${message}\nSee: ${url}`);
27
+ this.name = 'AppKitError';
28
+ this.module = module;
29
+ this.docsUrl = url;
30
+ }
31
+ }
32
+ /**
33
+ * Throw if a required env var is missing. Use at module init / first .get()
34
+ * call so the error is loud and immediate, not deferred to the first request.
35
+ *
36
+ * @example
37
+ * requireEnv('Auth', 'BLOOM_AUTH_SECRET',
38
+ * 'Set this to a 32+ character random string. See examples/.env.example.');
39
+ */
40
+ export function requireEnv(module, varName, hint) {
41
+ const value = process.env[varName];
42
+ if (value === undefined || value === '') {
43
+ const msg = hint
44
+ ? `requires env var \`${varName}\` to be set. ${hint}`
45
+ : `requires env var \`${varName}\` to be set.`;
46
+ throw new AppKitError(module, msg, 'environment-variables');
47
+ }
48
+ return value;
49
+ }
50
+ /**
51
+ * Throw if `value` is missing or wrongly typed. Use at the top of public
52
+ * methods that depend on caller-provided data.
53
+ *
54
+ * @example
55
+ * const auth = authClass.get();
56
+ * auth.signToken = (payload) => {
57
+ * requireProp('Auth', 'signToken.payload', payload);
58
+ * // ...
59
+ * };
60
+ */
61
+ export function requireProp(module, prop, value, hint) {
62
+ if (value === undefined || value === null) {
63
+ const msg = hint
64
+ ? `\`${prop}\` is required. ${hint}`
65
+ : `\`${prop}\` is required.`;
66
+ throw new AppKitError(module, msg);
67
+ }
68
+ return value;
69
+ }
70
+ /**
71
+ * Lighter-weight version that only warns in development. Use for nice-to-have
72
+ * conventions ("you should pass a 32-char secret, not a 16-char one") that
73
+ * shouldn't crash a production app on startup.
74
+ */
75
+ export function warnInDev(module, message, slug) {
76
+ if (process.env.NODE_ENV === 'production')
77
+ return;
78
+ const url = slug ? `${DOCS_BASE}#${slug}` : DOCS_BASE;
79
+ // eslint-disable-next-line no-console
80
+ console.warn(`[@bloomneo/appkit] ${module}: ${message}\nSee: ${url}`);
81
+ }
82
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/util/errors.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,MAAM,SAAS,GAAG,wDAAwD,CAAC;AAE3E,MAAM,OAAO,WAAY,SAAQ,KAAK;IAC3B,MAAM,CAAS;IACf,OAAO,CAAS;IAEzB,YAAY,MAAc,EAAE,OAAe,EAAE,IAAa;QACxD,MAAM,GAAG,GAAG,IAAI,CAAC,CAAC,CAAC,GAAG,SAAS,IAAI,IAAI,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;QACtD,KAAK,CAAC,sBAAsB,MAAM,KAAK,OAAO,UAAU,GAAG,EAAE,CAAC,CAAC;QAC/D,IAAI,CAAC,IAAI,GAAG,aAAa,CAAC;QAC1B,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,OAAO,GAAG,GAAG,CAAC;IACrB,CAAC;CACF;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,UAAU,CAAC,MAAc,EAAE,OAAe,EAAE,IAAa;IACvE,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;IACnC,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,EAAE,EAAE,CAAC;QACxC,MAAM,GAAG,GAAG,IAAI;YACd,CAAC,CAAC,sBAAsB,OAAO,iBAAiB,IAAI,EAAE;YACtD,CAAC,CAAC,sBAAsB,OAAO,eAAe,CAAC;QACjD,MAAM,IAAI,WAAW,CAAC,MAAM,EAAE,GAAG,EAAE,uBAAuB,CAAC,CAAC;IAC9D,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,WAAW,CACzB,MAAc,EACd,IAAY,EACZ,KAA2B,EAC3B,IAAa;IAEb,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;QAC1C,MAAM,GAAG,GAAG,IAAI;YACd,CAAC,CAAC,KAAK,IAAI,mBAAmB,IAAI,EAAE;YACpC,CAAC,CAAC,KAAK,IAAI,iBAAiB,CAAC;QAC/B,MAAM,IAAI,WAAW,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IACrC,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,SAAS,CAAC,MAAc,EAAE,OAAe,EAAE,IAAa;IACtE,IAAI,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,YAAY;QAAE,OAAO;IAClD,MAAM,GAAG,GAAG,IAAI,CAAC,CAAC,CAAC,GAAG,SAAS,IAAI,IAAI,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;IACtD,sCAAsC;IACtC,OAAO,CAAC,IAAI,CAAC,sBAAsB,MAAM,KAAK,OAAO,UAAU,GAAG,EAAE,CAAC,CAAC;AACxE,CAAC"}
@@ -0,0 +1,80 @@
1
+ # Bloomneo AppKit — canonical environment template
2
+ #
3
+ # Copy this to `.env` in your project root and fill in real values.
4
+ # All env vars use the BLOOM_ prefix. The legacy VOILA_ prefix from
5
+ # @voilajsx/appkit was removed in 1.5.2 — there is no fallback.
6
+ #
7
+ # Generate secrets with: openssl rand -base64 32
8
+
9
+ # ──────────────────────────────────────────────────────────────────────
10
+ # REQUIRED for production
11
+ # ──────────────────────────────────────────────────────────────────────
12
+
13
+ # JWT signing key for authClass.get().signToken() / verifyToken()
14
+ # Minimum 32 characters.
15
+ BLOOM_AUTH_SECRET=replace-me-with-openssl-rand-base64-32
16
+
17
+ # Prisma connection string. Any Prisma-supported URL works.
18
+ DATABASE_URL=postgresql://user:pass@localhost:5432/myapp
19
+
20
+ # CSRF token signing for securityClass.get().csrf() / requireCsrf()
21
+ # Minimum 32 characters.
22
+ BLOOM_SECURITY_CSRF_SECRET=replace-me-with-openssl-rand-base64-32
23
+
24
+ # AES-256-GCM encryption key for securityClass.get().encrypt() / decrypt()
25
+ # Exactly 64 hex characters (32 bytes).
26
+ BLOOM_SECURITY_ENCRYPTION_KEY=replace-me-with-openssl-rand-hex-32
27
+
28
+ # ──────────────────────────────────────────────────────────────────────
29
+ # OPTIONAL — auto-scaling kicks in when set
30
+ # ──────────────────────────────────────────────────────────────────────
31
+
32
+ # Distributed cache + queue + events (memory by default)
33
+ # REDIS_URL=redis://localhost:6379
34
+
35
+ # Cloud storage (local disk by default)
36
+ # AWS_S3_BUCKET=my-bucket
37
+ # AWS_REGION=us-east-1
38
+ # AWS_ACCESS_KEY_ID=...
39
+ # AWS_SECRET_ACCESS_KEY=...
40
+
41
+ # Cloudflare R2 (alternative to S3)
42
+ # R2_BUCKET=my-bucket
43
+ # R2_ACCOUNT_ID=...
44
+ # R2_ACCESS_KEY_ID=...
45
+ # R2_SECRET_ACCESS_KEY=...
46
+
47
+ # Production email (console transport by default)
48
+ # RESEND_API_KEY=re_...
49
+ # or
50
+ # SMTP_HOST=smtp.example.com
51
+ # SMTP_PORT=587
52
+ # SMTP_USER=...
53
+ # SMTP_PASS=...
54
+
55
+ # Multi-tenant database filtering
56
+ # When set to "auto", database queries auto-filter by tenant_id.
57
+ # BLOOM_DB_TENANT=auto
58
+
59
+ # Per-organization databases (multi-org SaaS)
60
+ # Each ORG_<NAME> is a separate connection string used by databaseClass.org('<name>').get()
61
+ # ORG_ACME=postgresql://acme.aws.com/prod
62
+ # ORG_GLOBEX=postgresql://globex.aws.com/prod
63
+
64
+ # Background queue persistence (memory by default)
65
+ # BLOOM_QUEUE_DB=true # use database-backed queue when REDIS_URL not set
66
+
67
+ # Logger transports
68
+ # BLOOM_LOGGER_FILE_PATH=/var/log/myapp.log
69
+ # BLOOM_LOGGER_HTTP_URL=https://logs.example.com/ingest
70
+ # BLOOM_LOGGER_WEBHOOK_URL=https://hooks.slack.com/services/...
71
+
72
+ # Service identification (used by logger metadata)
73
+ # BLOOM_SERVICE_NAME=myapp
74
+ # BLOOM_SERVICE_VERSION=1.0.0
75
+
76
+ # Default user password for seeds (development only)
77
+ # DEFAULT_USER_PASSWORD=changeme123
78
+
79
+ # Frontend API key (for browser → API auth — generated by `appkit generate app`)
80
+ # BLOOM_FRONTEND_KEY=bloom_<random>
@@ -0,0 +1,16 @@
1
+ # Examples
2
+
3
+ One minimal `.ts` file per module. Each example is the smallest runnable shape
4
+ of the canonical pattern — copy, modify the data, ship.
5
+
6
+ For composed multi-module patterns (auth-protected CRUD, multi-tenant API,
7
+ file upload + queue, real-time chat), see [`../cookbook/`](../cookbook/).
8
+
9
+ ## Conventions
10
+
11
+ - Always import from `@bloomneo/appkit` (or the subpath form).
12
+ - Always use `xxxClass.get()` to obtain a module instance.
13
+ - Always cache the module instance at module scope, not inside route handlers.
14
+ - Use semantic error throwers (`error.badRequest()`, `error.unauthorized()`),
15
+ never `throw new Error(...)` in route handlers.
16
+ - See [`.env.example`](./.env.example) for the canonical environment template.