@platformatic/foundation 3.63.0-alpha.3 → 3.64.0

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.
package/index.d.ts CHANGED
@@ -78,13 +78,24 @@ export interface ValidationError {
78
78
  export type ConfigurationOptions<T = {}> = Partial<{
79
79
  validate: boolean
80
80
  validationOptions: object
81
- transform: (config: Configuration<T>) => Promise<Configuration<T>> | Configuration<T>
81
+ // Read directly off the context by every capability package (astro, basic,
82
+ // next, node, service, ...) to pick a development/production code path.
83
+ isProduction: boolean
84
+ // loadConfiguration() always invokes the caller-supplied transform hook
85
+ // with all three arguments (config, schema, options), mirroring the
86
+ // exported transform() function's own parameter list.
87
+ transform: (
88
+ config: Configuration<T>,
89
+ schema: object,
90
+ options: ConfigurationOptions<T>
91
+ ) => Promise<Configuration<T>> | Configuration<T>
82
92
  upgrade: (logger: Logger, config: RawConfiguration, version: string) => Promise<RawConfiguration> | RawConfiguration
83
93
  env: Record<string, string>
84
94
  ignoreProcessEnv: boolean
85
95
  replaceEnv: boolean
86
96
  replaceEnvIgnore: string[]
87
97
  onMissingEnv: (key: string) => string | undefined
98
+ strictEnv: boolean | 'warn'
88
99
  fixPaths: boolean
89
100
  logger: Logger
90
101
  root: string
@@ -154,9 +165,13 @@ export declare function replaceEnv (
154
165
  onMissingEnv?: (key: string) => string | undefined,
155
166
  ignore?: string[]
156
167
  ): RawConfiguration
168
+ // `config` also accepts an array: the exported `applications` schema is a
169
+ // JSONSchemaType<object[]> (an ARRAY schema), and validate() is called with
170
+ // a matching array of application configs before they are handed to
171
+ // prepareApplication()/addApplications().
157
172
  export declare function validate (
158
173
  schema: JSONSchemaType<any>,
159
- config: RawConfiguration,
174
+ config: RawConfiguration | RawConfiguration[],
160
175
  validationOptions?: object,
161
176
  fixPaths?: boolean,
162
177
  root?: string
@@ -166,7 +181,22 @@ export declare function loadConfiguration (
166
181
  schema?: any,
167
182
  options?: ConfigurationOptions
168
183
  ): Promise<Configuration>
169
- export declare function loadConfigurationModule (root: string, config: RawConfiguration | ModuleWithVersion): any
184
+
185
+ // The capability module object returned by loadConfigurationModule(): the
186
+ // object a capability exports (its shape is otherwise capability-defined,
187
+ // hence the index signature).
188
+ export interface ConfigurationModule {
189
+ loadConfiguration?: (configPath: string) => Promise<unknown>
190
+ skipTelemetryHooks?: boolean
191
+ createCommands?: (applicationId: string) => unknown
192
+ modulesToLoad?: string[]
193
+ [key: string]: unknown
194
+ }
195
+ export declare function loadConfigurationModule (
196
+ root: string,
197
+ config: RawConfiguration | ModuleWithVersion,
198
+ pkg?: string
199
+ ): Promise<ConfigurationModule>
170
200
 
171
201
  // Error types
172
202
  export declare const ERROR_PREFIX: string
@@ -181,6 +211,7 @@ export declare const SourceMissingError: FastifyError
181
211
  export declare const RootMissingError: FastifyError
182
212
  export declare const SchemaMustBeDefinedError: FastifyError
183
213
  export declare const ConfigurationDoesNotValidateAgainstSchemaError: FastifyError
214
+ export declare const MissingEnvVariablesError: FastifyError
184
215
 
185
216
  // Execution types
186
217
  export declare function executeWithTimeout<T> (promise: Promise<T>, timeout: number, timeoutValue?: any): Promise<T>
@@ -12,6 +12,7 @@ import {
12
12
  CannotParseConfigFileError,
13
13
  ConfigurationDoesNotValidateAgainstSchemaError,
14
14
  InvalidConfigFileExtensionError,
15
+ MissingEnvVariablesError,
15
16
  RootMissingError,
16
17
  SourceMissingError
17
18
  } from './errors.js'
@@ -382,10 +383,13 @@ export async function loadEnv (root, ignoreProcessEnv = false, additionalEnv = {
382
383
  const baseEnv = ignoreProcessEnv ? {} : process.env
383
384
  const envFromFile = envFile ? parseEnv(await readFile(envFile, 'utf-8')) : {}
384
385
 
386
+ // The env file provides fallback defaults: variables already set in the real
387
+ // environment (and explicit programmatic values) take precedence over it,
388
+ // matching the dotenv/docker-compose/Vite convention.
385
389
  return {
390
+ ...envFromFile,
386
391
  ...baseEnv,
387
- ...additionalEnv,
388
- ...envFromFile
392
+ ...additionalEnv
389
393
  }
390
394
  }
391
395
 
@@ -442,6 +446,16 @@ export function replaceEnv (config, env, onMissingEnv, ignore) {
442
446
  return config
443
447
  }
444
448
 
449
+ function normalizeStrictEnv (value) {
450
+ if (value === 'warn') {
451
+ return 'warn'
452
+ } else if (value === 'false' || value === '') {
453
+ return false
454
+ }
455
+
456
+ return Boolean(value)
457
+ }
458
+
445
459
  export async function loadConfiguration (source, schema, options = {}) {
446
460
  const {
447
461
  validate: shouldValidate,
@@ -453,6 +467,7 @@ export async function loadConfiguration (source, schema, options = {}) {
453
467
  replaceEnv: shouldReplaceEnv,
454
468
  replaceEnvIgnore,
455
469
  onMissingEnv,
470
+ strictEnv: strictEnvOption,
456
471
  fixPaths,
457
472
  logger,
458
473
  skipMetadata,
@@ -487,7 +502,42 @@ export async function loadConfiguration (source, schema, options = {}) {
487
502
  env.PLT_ROOT = root
488
503
 
489
504
  if (shouldReplaceEnv) {
490
- config = replaceEnv(config, env, onMissingEnv, replaceEnvIgnore)
505
+ const missingEnv = new Set()
506
+
507
+ config = replaceEnv(
508
+ config,
509
+ env,
510
+ key => {
511
+ const value = onMissingEnv?.(key)
512
+
513
+ if (typeof value === 'undefined' || value === null) {
514
+ missingEnv.add(key)
515
+ }
516
+
517
+ return value
518
+ },
519
+ replaceEnvIgnore
520
+ )
521
+
522
+ // strictEnv can be set programmatically or in the configuration file itself, either at the top level
523
+ // (runtime configurations) or in the runtime property (capabilities configurations).
524
+ const strictEnv = normalizeStrictEnv(strictEnvOption ?? config.strictEnv ?? config.runtime?.strictEnv)
525
+
526
+ if (strictEnv && missingEnv.size > 0) {
527
+ const keys = Array.from(missingEnv).sort().join(', ')
528
+
529
+ if (strictEnv === 'warn') {
530
+ const message = `The configuration references the following environment variables which are not set: ${keys}`
531
+
532
+ if (logger) {
533
+ logger.warn(message)
534
+ } else {
535
+ process.emitWarning(message)
536
+ }
537
+ } else {
538
+ throw new MissingEnvVariablesError(keys)
539
+ }
540
+ }
491
541
  }
492
542
 
493
543
  const moduleInfo = extractModuleFromSchemaUrl(config)
package/lib/errors.js CHANGED
@@ -56,3 +56,7 @@ export const ConfigurationDoesNotValidateAgainstSchemaError = createError(
56
56
  `${ERROR_PREFIX}_CONFIGURATION_DOES_NOT_VALIDATE_AGAINST_SCHEMA`,
57
57
  'The configuration does not validate against the configuration schema'
58
58
  )
59
+ export const MissingEnvVariablesError = createError(
60
+ `${ERROR_PREFIX}_MISSING_ENV_VARIABLES`,
61
+ 'The configuration references the following environment variables which are not set: %s'
62
+ )
package/lib/module.js CHANGED
@@ -17,6 +17,11 @@ export const applicationTypes = [
17
17
  { name: '@platformatic/next', label: 'Next.js', dependencies: ['next'] },
18
18
  { name: '@platformatic/remix', label: 'Remix', dependencies: ['@remix-run/dev'] },
19
19
  { name: '@platformatic/astro', label: 'Astro', dependencies: ['astro'] },
20
+ { name: '@platformatic/react-router', label: 'React Router', dependencies: ['@react-router/dev'] },
21
+ { name: '@platformatic/nuxt', label: 'Nuxt', dependencies: ['nuxt'] },
22
+ { name: '@platformatic/tanstack', label: 'TanStack Start', dependencies: ['@tanstack/react-start'] },
23
+ // Nitro applications often use Vite, so Nitro must be checked first.
24
+ { name: '@platformatic/nitro', label: 'Nitro', dependencies: ['nitro', 'nitropack'] },
20
25
  // Since Vite is often used with other frameworks, we must check for Vite last amongst frontend frameworks
21
26
  { name: '@platformatic/vite', label: 'Vite', dependencies: ['vite'] },
22
27
  {
package/lib/schema.js CHANGED
@@ -396,7 +396,8 @@ export const server = {
396
396
  portAssignment: {
397
397
  type: 'string',
398
398
  enum: ['shared', 'perWorkerIncrement'],
399
- description: 'Configures how entrypoint server worker ports are assigned. When set to shared, all workers listen on the same port. When set to perWorkerIncrement, each worker will use its own port, starting from port (worker 0).'
399
+ description:
400
+ 'Configures how entrypoint server worker ports are assigned. When set to shared, all workers listen on the same port. When set to perWorkerIncrement, each worker will use its own port, starting from port (worker 0).'
400
401
  },
401
402
  backlog: {
402
403
  type: 'integer',
@@ -595,6 +596,18 @@ export const fastifyServer = {
595
596
  }
596
597
  }
597
598
  },
599
+ ajv: {
600
+ type: 'object',
601
+ description:
602
+ 'Options for the Fastify request-validation Ajv instance (the Fastify `ajv` server option). Only `customOptions` is configurable from the config file; for example set `customOptions.coerceTypes` to `false` to reject empty strings on fields that allow the `null` type instead of coercing them to `null`.',
603
+ properties: {
604
+ customOptions: {
605
+ type: 'object',
606
+ additionalProperties: true
607
+ }
608
+ },
609
+ additionalProperties: false
610
+ },
598
611
  caseSensitive: {
599
612
  type: 'boolean'
600
613
  },
@@ -791,7 +804,8 @@ export const telemetry = {
791
804
  type: 'string'
792
805
  }
793
806
  ],
794
- description: 'Enable the OpenTelemetry diagnostic logger. Diagnostic messages are forwarded to the Platformatic global logger using the current logger level.'
807
+ description:
808
+ 'Enable the OpenTelemetry diagnostic logger. Diagnostic messages are forwarded to the Platformatic global logger using the current logger level.'
795
809
  }
796
810
  },
797
811
  required: ['applicationName'],
@@ -1125,7 +1139,7 @@ export const runtimeProperties = {
1125
1139
  },
1126
1140
  { type: 'string' }
1127
1141
  ],
1128
- default: 10000
1142
+ default: 30000
1129
1143
  },
1130
1144
  application: {
1131
1145
  anyOf: [
@@ -1533,6 +1547,11 @@ export const runtimeProperties = {
1533
1547
  envfile: {
1534
1548
  type: 'string'
1535
1549
  },
1550
+ strictEnv: {
1551
+ anyOf: [{ type: 'boolean' }, { type: 'string' }],
1552
+ description:
1553
+ 'When set to true, the configuration loading fails if a {PLT_*} placeholder references an environment variable which is not set. When set to "warn", a warning listing the missing variables is logged but the placeholders are still replaced with an empty string. Defaults to false.'
1554
+ },
1536
1555
  sourceMaps: {
1537
1556
  type: 'boolean',
1538
1557
  default: false
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@platformatic/foundation",
3
- "version": "3.63.0-alpha.3",
3
+ "version": "3.64.0",
4
4
  "description": "Platformatic Foundation",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",
@@ -37,7 +37,7 @@
37
37
  "c8": "^11.0.0",
38
38
  "cleaner-spec-reporter": "^0.5.0",
39
39
  "eslint": "9",
40
- "fastify": "^5.7.0",
40
+ "fastify": "^5.0.0",
41
41
  "neostandard": "^0.12.0",
42
42
  "pino": "^9.9.0",
43
43
  "pino-test": "^1.0.1",