@platformatic/remix 3.4.1 → 3.5.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/config.d.ts CHANGED
@@ -5,7 +5,7 @@
5
5
  * and run json-schema-to-typescript to regenerate this file.
6
6
  */
7
7
 
8
- export interface PlatformaticRemixStackable {
8
+ export interface PlatformaticRemixConfig {
9
9
  $schema?: string;
10
10
  logger?: {
11
11
  level: (
@@ -29,8 +29,6 @@ export interface PlatformaticRemixStackable {
29
29
  [k: string]: unknown;
30
30
  };
31
31
  level?: string;
32
- additionalProperties?: never;
33
- [k: string]: unknown;
34
32
  }[];
35
33
  options?: {
36
34
  [k: string]: unknown;
@@ -42,6 +40,21 @@ export interface PlatformaticRemixStackable {
42
40
  [k: string]: unknown;
43
41
  };
44
42
  };
43
+ formatters?: {
44
+ path: string;
45
+ };
46
+ timestamp?: "epochTime" | "unixTime" | "nullTime" | "isoTime";
47
+ redact?: {
48
+ paths: string[];
49
+ censor?: string;
50
+ };
51
+ base?: {
52
+ [k: string]: unknown;
53
+ } | null;
54
+ messageKey?: string;
55
+ customLevels?: {
56
+ [k: string]: unknown;
57
+ };
45
58
  [k: string]: unknown;
46
59
  };
47
60
  server?: {
@@ -49,6 +62,7 @@ export interface PlatformaticRemixStackable {
49
62
  port?: number | string;
50
63
  http2?: boolean;
51
64
  https?: {
65
+ allowHTTP1?: boolean;
52
66
  key:
53
67
  | string
54
68
  | {
@@ -71,6 +85,8 @@ export interface PlatformaticRemixStackable {
71
85
  path?: string;
72
86
  }
73
87
  )[];
88
+ requestCert?: boolean;
89
+ rejectUnauthorized?: boolean;
74
90
  };
75
91
  };
76
92
  watch?:
@@ -95,6 +111,326 @@ export interface PlatformaticRemixStackable {
95
111
  production?: string;
96
112
  };
97
113
  };
114
+ runtime?: {
115
+ preload?: string | string[];
116
+ basePath?: string;
117
+ services?: {
118
+ [k: string]: unknown;
119
+ }[];
120
+ workers?: number | string;
121
+ logger?: {
122
+ level: (
123
+ | ("fatal" | "error" | "warn" | "info" | "debug" | "trace" | "silent")
124
+ | {
125
+ [k: string]: unknown;
126
+ }
127
+ ) &
128
+ string;
129
+ transport?:
130
+ | {
131
+ target?: string;
132
+ options?: {
133
+ [k: string]: unknown;
134
+ };
135
+ }
136
+ | {
137
+ targets?: {
138
+ target?: string;
139
+ options?: {
140
+ [k: string]: unknown;
141
+ };
142
+ level?: string;
143
+ }[];
144
+ options?: {
145
+ [k: string]: unknown;
146
+ };
147
+ };
148
+ pipeline?: {
149
+ target?: string;
150
+ options?: {
151
+ [k: string]: unknown;
152
+ };
153
+ };
154
+ formatters?: {
155
+ path: string;
156
+ };
157
+ timestamp?: "epochTime" | "unixTime" | "nullTime" | "isoTime";
158
+ redact?: {
159
+ paths: string[];
160
+ censor?: string;
161
+ };
162
+ base?: {
163
+ [k: string]: unknown;
164
+ } | null;
165
+ messageKey?: string;
166
+ customLevels?: {
167
+ [k: string]: unknown;
168
+ };
169
+ [k: string]: unknown;
170
+ };
171
+ server?: {
172
+ hostname?: string;
173
+ port?: number | string;
174
+ http2?: boolean;
175
+ https?: {
176
+ allowHTTP1?: boolean;
177
+ key:
178
+ | string
179
+ | {
180
+ path?: string;
181
+ }
182
+ | (
183
+ | string
184
+ | {
185
+ path?: string;
186
+ }
187
+ )[];
188
+ cert:
189
+ | string
190
+ | {
191
+ path?: string;
192
+ }
193
+ | (
194
+ | string
195
+ | {
196
+ path?: string;
197
+ }
198
+ )[];
199
+ requestCert?: boolean;
200
+ rejectUnauthorized?: boolean;
201
+ };
202
+ };
203
+ startTimeout?: number;
204
+ restartOnError?: boolean | number;
205
+ exitOnUnhandledErrors?: boolean;
206
+ gracefulShutdown?: {
207
+ runtime: number | string;
208
+ application: number | string;
209
+ };
210
+ health?: {
211
+ enabled?: boolean | string;
212
+ interval?: number | string;
213
+ gracePeriod?: number | string;
214
+ maxUnhealthyChecks?: number | string;
215
+ maxELU?: number | string;
216
+ maxHeapUsed?: number | string;
217
+ maxHeapTotal?: number | string;
218
+ maxYoungGeneration?: number | string;
219
+ };
220
+ undici?: {
221
+ agentOptions?: {
222
+ [k: string]: unknown;
223
+ };
224
+ interceptors?:
225
+ | {
226
+ module: string;
227
+ options: {
228
+ [k: string]: unknown;
229
+ };
230
+ [k: string]: unknown;
231
+ }[]
232
+ | {
233
+ Client?: {
234
+ module: string;
235
+ options: {
236
+ [k: string]: unknown;
237
+ };
238
+ [k: string]: unknown;
239
+ }[];
240
+ Pool?: {
241
+ module: string;
242
+ options: {
243
+ [k: string]: unknown;
244
+ };
245
+ [k: string]: unknown;
246
+ }[];
247
+ Agent?: {
248
+ module: string;
249
+ options: {
250
+ [k: string]: unknown;
251
+ };
252
+ [k: string]: unknown;
253
+ }[];
254
+ [k: string]: unknown;
255
+ };
256
+ [k: string]: unknown;
257
+ };
258
+ httpCache?:
259
+ | boolean
260
+ | {
261
+ store?: string;
262
+ /**
263
+ * @minItems 1
264
+ */
265
+ methods?: [string, ...string[]];
266
+ cacheTagsHeader?: string;
267
+ maxSize?: number;
268
+ maxEntrySize?: number;
269
+ maxCount?: number;
270
+ [k: string]: unknown;
271
+ };
272
+ watch?: boolean | string;
273
+ managementApi?:
274
+ | boolean
275
+ | string
276
+ | {
277
+ logs?: {
278
+ maxSize?: number;
279
+ };
280
+ };
281
+ metrics?:
282
+ | boolean
283
+ | {
284
+ port?: number | string;
285
+ enabled?: boolean | string;
286
+ hostname?: string;
287
+ endpoint?: string;
288
+ auth?: {
289
+ username: string;
290
+ password: string;
291
+ };
292
+ labels?: {
293
+ [k: string]: string;
294
+ };
295
+ /**
296
+ * The label name to use for the application identifier in metrics (e.g., applicationId, serviceId)
297
+ */
298
+ applicationLabel?: string;
299
+ readiness?:
300
+ | boolean
301
+ | {
302
+ endpoint?: string;
303
+ success?: {
304
+ statusCode?: number;
305
+ body?: string;
306
+ };
307
+ fail?: {
308
+ statusCode?: number;
309
+ body?: string;
310
+ };
311
+ };
312
+ liveness?:
313
+ | boolean
314
+ | {
315
+ endpoint?: string;
316
+ success?: {
317
+ statusCode?: number;
318
+ body?: string;
319
+ };
320
+ fail?: {
321
+ statusCode?: number;
322
+ body?: string;
323
+ };
324
+ };
325
+ plugins?: string[];
326
+ };
327
+ telemetry?: {
328
+ enabled?: boolean | string;
329
+ /**
330
+ * The name of the application. Defaults to the folder name if not specified.
331
+ */
332
+ applicationName: string;
333
+ /**
334
+ * The version of the application (optional)
335
+ */
336
+ version?: string;
337
+ /**
338
+ * An array of paths to skip when creating spans. Useful for health checks and other endpoints that do not need to be traced.
339
+ */
340
+ skip?: {
341
+ /**
342
+ * The path to skip. Can be a string or a regex.
343
+ */
344
+ path?: string;
345
+ /**
346
+ * HTTP method to skip
347
+ */
348
+ method?: "GET" | "POST" | "PUT" | "DELETE" | "PATCH" | "HEAD" | "OPTIONS";
349
+ [k: string]: unknown;
350
+ }[];
351
+ exporter?:
352
+ | {
353
+ type?: "console" | "otlp" | "zipkin" | "memory" | "file";
354
+ /**
355
+ * Options for the exporter. These are passed directly to the exporter.
356
+ */
357
+ options?: {
358
+ /**
359
+ * The URL to send the traces to. Not used for console or memory exporters.
360
+ */
361
+ url?: string;
362
+ /**
363
+ * Headers to send to the exporter. Not used for console or memory exporters.
364
+ */
365
+ headers?: {
366
+ [k: string]: unknown;
367
+ };
368
+ /**
369
+ * The path to write the traces to. Only for file exporter.
370
+ */
371
+ path?: string;
372
+ [k: string]: unknown;
373
+ };
374
+ additionalProperties?: never;
375
+ [k: string]: unknown;
376
+ }[]
377
+ | {
378
+ type?: "console" | "otlp" | "zipkin" | "memory" | "file";
379
+ /**
380
+ * Options for the exporter. These are passed directly to the exporter.
381
+ */
382
+ options?: {
383
+ /**
384
+ * The URL to send the traces to. Not used for console or memory exporters.
385
+ */
386
+ url?: string;
387
+ /**
388
+ * Headers to send to the exporter. Not used for console or memory exporters.
389
+ */
390
+ headers?: {
391
+ [k: string]: unknown;
392
+ };
393
+ /**
394
+ * The path to write the traces to. Only for file exporter.
395
+ */
396
+ path?: string;
397
+ [k: string]: unknown;
398
+ };
399
+ additionalProperties?: never;
400
+ [k: string]: unknown;
401
+ };
402
+ };
403
+ inspectorOptions?: {
404
+ host?: string;
405
+ port?: number;
406
+ breakFirstLine?: boolean;
407
+ watchDisabled?: boolean;
408
+ [k: string]: unknown;
409
+ };
410
+ applicationTimeout?: number | string;
411
+ messagingTimeout?: number | string;
412
+ env?: {
413
+ [k: string]: string;
414
+ };
415
+ sourceMaps?: boolean;
416
+ scheduler?: {
417
+ enabled?: boolean | string;
418
+ name: string;
419
+ cron: string;
420
+ callbackUrl: string;
421
+ method?: "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
422
+ headers?: {
423
+ [k: string]: string;
424
+ };
425
+ body?:
426
+ | string
427
+ | {
428
+ [k: string]: unknown;
429
+ };
430
+ maxRetries?: number;
431
+ [k: string]: unknown;
432
+ }[];
433
+ };
98
434
  vite?: {
99
435
  configFile?: string | boolean;
100
436
  devServer?: {
package/index.js CHANGED
@@ -1,248 +1,31 @@
1
- import {
2
- cleanBasePath,
3
- ensureTrailingSlash,
4
- errors,
5
- getServerUrl,
6
- importFile,
7
- resolvePackage,
8
- schemaOptions
9
- } from '@platformatic/basic'
10
- import { ConfigManager } from '@platformatic/config'
11
- import { ViteStackable } from '@platformatic/vite'
12
- import { createRequestHandler } from '@remix-run/express'
13
- import express from 'express'
14
- import inject from 'light-my-request'
15
- import { readFile } from 'node:fs/promises'
16
- import { dirname, resolve } from 'node:path'
17
- import { pathToFileURL } from 'node:url'
18
- import { pinoHttp } from 'pino-http'
19
- import { satisfies } from 'semver'
20
- import { packageJson, schema } from './lib/schema.js'
21
-
22
- const supportedVersions = '^2.0.0'
23
-
24
- export class RemixStackable extends ViteStackable {
25
- #app
26
- #server
27
- #dispatcher
28
- #remix
29
- #basePath
30
-
31
- constructor (options, root, configManager) {
32
- super(options, root, configManager)
33
-
34
- this.type = 'remix'
35
- this.version = packageJson.version
36
- }
37
-
38
- async init () {
39
- await super.init()
40
-
41
- this.#remix = resolve(dirname(resolvePackage(this.root, '@remix-run/dev')), '..')
42
- const remixPackage = JSON.parse(await readFile(resolve(this.#remix, 'package.json'), 'utf-8'))
43
-
44
- /* c8 ignore next 3 */
45
- if (!satisfies(remixPackage.version, supportedVersions)) {
46
- throw new errors.UnsupportedVersion('@remix-run/dev', remixPackage.version, supportedVersions)
47
- }
48
-
49
- const config = this.configManager.current
50
- this.#basePath = config.application?.basePath
51
- ? ensureTrailingSlash(cleanBasePath(config.application?.basePath))
52
- : undefined
53
-
54
- this.registerGlobals({
55
- id: this.id,
56
- // Always use URL to avoid serialization problem in Windows
57
- root: pathToFileURL(this.root).toString(),
58
- basePath: this.#basePath,
59
- logLevel: this.logger.level
60
- })
61
-
62
- this.subprocessTerminationSignal = 'SIGKILL'
63
- }
64
-
65
- async start ({ listen }) {
66
- // Make this idempotent
67
- if (this.url) {
68
- return this.url
69
- }
70
-
71
- const config = this.configManager.current
72
- const command = config.application.commands[this.isProduction ? 'production' : 'development']
73
-
74
- if (command) {
75
- return this.startWithCommand(command)
76
- }
77
-
78
- if (this.isProduction) {
79
- return this.#startProduction(listen)
80
- }
81
-
82
- const { preloadViteEsm } = await importFile(resolve(this.#remix, './dist/vite/import-vite-esm-sync.js'))
83
- await preloadViteEsm()
84
- await super.start({ listen })
85
-
86
- /* c8 ignore next 3 */
87
- if (!this._getVite().config.plugins.some(p => p.name === 'remix')) {
88
- this.logger.warn('Could not find Remix plugin in your Vite configuration. Continuing as plain Vite application.')
89
- }
90
- }
91
-
92
- async stop () {
93
- if (this.subprocess) {
94
- return this.stopCommand()
95
- }
96
-
97
- if (this.isProduction) {
98
- return this.#stopProduction()
99
- }
100
-
101
- return super.stop()
102
- }
103
-
104
- async build () {
105
- const config = this.configManager.current
106
- const command = config.application.commands.build
107
- const basePath = config.application?.basePath
108
- ? ensureTrailingSlash(cleanBasePath(config.application?.basePath))
109
- : undefined
110
-
111
- if (command) {
112
- return this.buildWithCommand(command, basePath)
113
- }
114
-
115
- await this.init()
116
- const { viteBuild } = await importFile(resolve(this.#remix, 'dist/cli/commands.js'))
117
-
118
- await viteBuild(this.root, {
119
- emptyOutDir: true,
120
- logLevel: this.logger.level,
121
- mode: 'production',
122
- profile: false
123
- })
124
- }
125
-
126
- async inject (injectParams, onInject) {
127
- if (!this.isProduction) {
128
- return super.inject(injectParams, onInject)
129
- }
130
-
131
- const res = await inject(this.#app, injectParams, onInject)
132
-
133
- /* c8 ignore next 3 */
134
- if (onInject) {
135
- return
136
- }
137
-
138
- // Since inject might be called from the main thread directly via ITC, let's clean it up
139
- const { statusCode, headers, body, payload, rawPayload } = res
140
-
141
- return { statusCode, headers, body, payload, rawPayload }
142
- }
143
-
144
- getMeta () {
145
- if (!this.isProduction) {
146
- return super.getMeta()
147
- }
148
-
149
- return {
150
- composer: {
151
- tcp: typeof this.url !== 'undefined',
152
- url: this.url,
153
- prefix: this.basePath ?? this.#basePath,
154
- wantsAbsoluteUrls: true,
155
- needsRootRedirect: true
156
- }
157
- }
158
- }
159
-
160
- async #startProduction (listen) {
161
- // Listen if entrypoint
162
- if (this.#app && listen) {
163
- const serverOptions = this.serverConfig
164
-
165
- this.#server = await new Promise((resolve, reject) => {
166
- return this.#app
167
- .listen({ host: serverOptions?.hostname || '127.0.0.1', port: serverOptions?.port || 0 }, function () {
168
- resolve(this)
169
- })
170
- .on('error', reject)
171
- })
172
-
173
- this.url = getServerUrl(this.#server)
174
-
175
- return this.url
176
- }
177
-
178
- const outputDirectory = this.configManager.current.remix.outputDirectory
179
- this.verifyOutputDirectory(resolve(this.root, outputDirectory))
180
-
181
- const build = await importFile(resolve(this.root, `${outputDirectory}/server/index.js`))
182
- this.#basePath = ensureTrailingSlash(cleanBasePath(build.basename))
183
-
184
- this.#app = express()
185
- this.#app.use(pinoHttp({ logger: this.logger }))
186
- this.#app.use(this.#basePath, express.static(resolve(this.root, `${outputDirectory}/client`)))
187
- this.#app.all(`${ensureTrailingSlash(cleanBasePath(this.#basePath))}*`, createRequestHandler({ build }))
188
-
189
- return this.url
190
- }
191
-
192
- async #stopProduction () {
193
- /* c8 ignore next 3 */
194
- if (!this.#server?.listening) {
195
- return
196
- }
197
-
198
- return new Promise((resolve, reject) => {
199
- this.#server.close(error => {
200
- /* c8 ignore next 3 */
201
- if (error) {
202
- return reject(error)
203
- }
204
-
205
- resolve()
206
- })
207
- })
208
- }
1
+ import { transform as basicTransform, resolve, validationOptions } from '@platformatic/basic'
2
+ import { kMetadata, loadConfiguration as utilsLoadConfiguration } from '@platformatic/foundation'
3
+ import { RemixCapability } from './lib/capability.js'
4
+ import { schema } from './lib/schema.js'
5
+
6
+ /* c8 ignore next 5 */
7
+ export async function transform (config, schema, options) {
8
+ config = await basicTransform(config, schema, options)
9
+ config.watch = { enabled: false }
10
+ return config
209
11
  }
210
12
 
211
- /* c8 ignore next 9 */
212
- function transformConfig () {
213
- if (this.current.watch === undefined) {
214
- this.current.watch = { enabled: false }
215
- }
13
+ export async function loadConfiguration (configOrRoot, sourceOrConfig, context) {
14
+ const { root, source } = await resolve(configOrRoot, sourceOrConfig, 'application')
216
15
 
217
- if (typeof this.current.watch !== 'object') {
218
- this.current.watch = { enabled: this.current.watch || false }
219
- }
220
- }
221
-
222
- export async function buildStackable (opts) {
223
- const root = opts.context.directory
224
-
225
- const configManager = new ConfigManager({
226
- schema,
227
- source: opts.config ?? {},
228
- schemaOptions,
229
- transformConfig,
230
- dirname: root
16
+ return utilsLoadConfiguration(source, context?.schema ?? schema, {
17
+ validationOptions,
18
+ transform,
19
+ replaceEnv: true,
20
+ root,
21
+ ...context
231
22
  })
232
- await configManager.parseAndValidate()
233
-
234
- return new RemixStackable(opts, root, configManager)
235
23
  }
236
24
 
237
- export { schema, schemaComponents } from './lib/schema.js'
238
-
239
- export default {
240
- configType: 'remix',
241
- configManagerConfig: {
242
- schemaOptions,
243
- transformConfig
244
- },
245
- buildStackable,
246
- schema,
247
- version: packageJson.version
25
+ export async function create (configOrRoot, sourceOrConfig, context) {
26
+ const config = await loadConfiguration(configOrRoot, sourceOrConfig, context)
27
+ return new RemixCapability(config[kMetadata].root, config, context)
248
28
  }
29
+
30
+ export * from './lib/capability.js'
31
+ export { packageJson, schema, schemaComponents, version } from './lib/schema.js'