@platformatic/next 3.72.0 → 4.0.0-alpha.1

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
@@ -1,4 +1,4 @@
1
- import { BaseCapability, BaseContext, BaseOptions } from '@platformatic/basic'
1
+ import { ApplicationDefinition, BaseCapability, BaseContext, BaseOptions, CapabilityFactoryOptions, ConfigContext, DeferredApplicationDefinition } from '@platformatic/basic'
2
2
  import { Configuration, ConfigurationOptions } from '@platformatic/foundation'
3
3
  import { JSONSchemaType } from 'ajv'
4
4
  import { FastifyError } from 'fastify'
@@ -6,6 +6,26 @@ import type { PlatformaticNextJsConfig } from './config.d.ts'
6
6
 
7
7
  export type { PlatformaticNextJsConfig } from './config.d.ts'
8
8
 
9
+ /*
10
+ The option types this capability's configuration is built from, generated from the audited schema.
11
+ `NextHttpsOptions` is Next's own and sits next to `AppServerOptions['https']`, which is
12
+ `HttpsOptions`: two adjacent keys spelled almost alike, which is exactly why both have names.
13
+ */
14
+ export type {
15
+ AppLoggerOptions,
16
+ AppServerOptions,
17
+ ApplicationHealthOptions,
18
+ ApplicationTelemetryOverrides,
19
+ ApplicationWorkersOptions,
20
+ BuildableApplicationOptions,
21
+ HttpsOptions,
22
+ ImageOptimizerOptions,
23
+ NextCacheOptions,
24
+ NextHttpsOptions,
25
+ PermissionsOptions,
26
+ WatchOptions
27
+ } from './config.d.ts'
28
+
9
29
  export interface NextContext extends BaseContext {}
10
30
 
11
31
  export type NextConfiguration = Configuration<PlatformaticNextJsConfig>
@@ -55,3 +75,10 @@ export namespace errors {
55
75
  export const StandaloneServerNotFound: () => FastifyError
56
76
  export const CannotParseStandaloneServer: () => FastifyError
57
77
  }
78
+
79
+ export type NextConfigOptions = CapabilityFactoryOptions<PlatformaticNextJsConfig, 'next', never>
80
+
81
+ export declare function createNextConfig (options?: NextConfigOptions): ApplicationDefinition
82
+ export declare function createNextConfig (
83
+ callback: (context: ConfigContext) => NextConfigOptions | Promise<NextConfigOptions>
84
+ ): DeferredApplicationDefinition
package/index.js CHANGED
@@ -57,6 +57,25 @@ function enhanceNextCacheConfig (nextConfig, modifications) {
57
57
  }
58
58
  }
59
59
 
60
+ function externalizeGlobals (nextConfig, nextVersion) {
61
+ const packageName = '@platformatic/globals'
62
+
63
+ if (nextVersion.major >= 15) {
64
+ nextConfig.serverExternalPackages ??= []
65
+
66
+ if (!nextConfig.serverExternalPackages.includes(packageName)) {
67
+ nextConfig.serverExternalPackages.push(packageName)
68
+ }
69
+ } else {
70
+ nextConfig.experimental ??= {}
71
+ nextConfig.experimental.serverComponentsExternalPackages ??= []
72
+
73
+ if (!nextConfig.experimental.serverComponentsExternalPackages.includes(packageName)) {
74
+ nextConfig.experimental.serverComponentsExternalPackages.push(packageName)
75
+ }
76
+ }
77
+ }
78
+
60
79
  export async function enhanceNextConfig (nextConfig, ...args) {
61
80
  // This is to avoid https://github.com/vercel/next.js/issues/76981
62
81
  Headers.prototype[Symbol.for('nodejs.util.inspect.custom')] = undefined
@@ -67,8 +86,11 @@ export async function enhanceNextConfig (nextConfig, ...args) {
67
86
 
68
87
  const basePath = getBasePath()
69
88
  const config = getConfig()
89
+ const nextVersion = getNextVersion()
70
90
  const notifyConfig = getNotifyConfig()
71
91
 
92
+ externalizeGlobals(nextConfig, nextVersion)
93
+
72
94
  if (typeof nextConfig.basePath === 'undefined') {
73
95
  nextConfig.basePath = basePath
74
96
  }
@@ -109,3 +131,5 @@ export * from './lib/config.js'
109
131
  export * as errors from './lib/errors.js'
110
132
  export * from './lib/image-optimizer.js'
111
133
  export { packageJson, schema, schemaComponents, version } from './lib/schema.js'
134
+
135
+ export * from './lib/factory.js'
@@ -16,8 +16,6 @@ export const CACHE_HIT_METRIC = { name: 'next_cache_valkey_hit_count', help: 'Ne
16
16
  export const CACHE_MISS_METRIC = { name: 'next_cache_valkey_miss_count', help: 'Next.js Cache (Valkey) Miss Count' }
17
17
  export const MAX_BATCH_SIZE = 100
18
18
 
19
- const NEXT_CACHE_TAGS_HEADER = 'x-next-cache-tags'
20
-
21
19
  export const sections = {
22
20
  values: 'values',
23
21
  tags: 'tags'
@@ -101,7 +99,7 @@ export class CacheHandler {
101
99
  } catch (e) {
102
100
  this.#cacheMissMetric?.inc()
103
101
  this.#logger.error({ err: ensureLoggableError(e) }, 'Cannot read cache value from Valkey')
104
- return
102
+ throw new Error('Cannot read cache value from Valkey', { cause: e })
105
103
  }
106
104
 
107
105
  let value
@@ -134,7 +132,7 @@ export class CacheHandler {
134
132
  }
135
133
 
136
134
  async set (cacheKey, value, ctx, isRedisKey) {
137
- const tags = this.#getTags(ctx.tags, value)
135
+ const tags = ctx.tags
138
136
  const revalidate = ctx.revalidate ?? ctx.cacheControl?.revalidate ?? value.revalidate ?? 0
139
137
 
140
138
  this.#logger.trace({ key: cacheKey, value, tags, revalidate }, 'cache set')
@@ -269,25 +267,6 @@ export class CacheHandler {
269
267
  }
270
268
  }
271
269
 
272
- #getTags (tags, value) {
273
- // Next.js stores page and route tags in this header instead of ctx.tags.
274
- const tagsHeader = value?.headers?.[NEXT_CACHE_TAGS_HEADER]
275
-
276
- if (typeof tagsHeader !== 'string') {
277
- return tags
278
- }
279
-
280
- const combinedTags = new Set(tags)
281
-
282
- for (const tag of tagsHeader.split(',')) {
283
- if (tag) {
284
- combinedTags.add(tag)
285
- }
286
- }
287
-
288
- return [...combinedTags]
289
- }
290
-
291
270
  async #refreshKey (key, value) {
292
271
  const life = Math.round((Date.now() - value.lastModified) / 1000)
293
272
  const expire = Math.min(value.revalidate - life, this.#maxTTL)
package/lib/capability.js CHANGED
@@ -75,13 +75,8 @@ export class NextCapability extends BaseCapability {
75
75
  }
76
76
  }
77
77
 
78
- async start ({ listen }) {
79
- // Make this idempotent
80
- if (this.url) {
81
- return this.url
82
- }
83
-
84
- await super._start({ listen })
78
+ async _start () {
79
+ await super._start()
85
80
 
86
81
  this.on('config', config => {
87
82
  this.#configModified = true
@@ -89,9 +84,9 @@ export class NextCapability extends BaseCapability {
89
84
  })
90
85
 
91
86
  if (this.isProduction) {
92
- await this.#startProduction(listen)
87
+ await this.#startProduction()
93
88
  } else {
94
- await this.#startDevelopment(listen)
89
+ await this.#startDevelopment()
95
90
  }
96
91
 
97
92
  await this._collectMetrics()
@@ -109,8 +104,8 @@ export class NextCapability extends BaseCapability {
109
104
  }
110
105
  }
111
106
 
112
- async stop () {
113
- await super.stop()
107
+ async _stop () {
108
+ await super._stop()
114
109
 
115
110
  if (this.subprocess) {
116
111
  return this.stopCommand()
@@ -179,14 +174,12 @@ export class NextCapability extends BaseCapability {
179
174
  }
180
175
 
181
176
  getMeta () {
182
- const gateway = { prefix: this.basePath ?? this.#basePath, wantsAbsoluteUrls: true, needsRootTrailingSlash: false }
183
-
184
- if (this.url) {
185
- gateway.tcp = true
186
- gateway.url = this.url
187
- }
188
-
189
- return { gateway }
177
+ return super.getMeta({
178
+ includeConnection: true,
179
+ prefix: this.basePath ?? this.#basePath,
180
+ wantsAbsoluteUrls: true,
181
+ needsRootTrailingSlash: false
182
+ })
190
183
  }
191
184
 
192
185
  async getChildManagerContext (basePath) {
@@ -211,7 +204,11 @@ export class NextCapability extends BaseCapability {
211
204
  return this.startWithCommand(command, loaderUrl, this.#getChildManagerScripts())
212
205
  }
213
206
 
214
- const { hostname, port } = this.serverConfig ?? {}
207
+ if (typeof this.serverConfig?.port === 'undefined') {
208
+ return
209
+ }
210
+
211
+ const { hostname, port } = this.serverConfig
215
212
  const serverOptions = {
216
213
  host: hostname || '127.0.0.1',
217
214
  port: port || 0
@@ -317,13 +314,13 @@ export class NextCapability extends BaseCapability {
317
314
 
318
315
  if (command) {
319
316
  const childManagerScripts = this.#getChildManagerScripts()
320
-
321
- if (this.#nextVersion.major < 15 || (this.#nextVersion.major <= 15 && this.#nextVersion.minor < 1)) {
322
- childManagerScripts.push(new URL('./create-context-patch.js', import.meta.url))
323
- }
324
317
  return this.startWithCommand(command, loaderUrl, childManagerScripts)
325
318
  }
326
319
 
320
+ if (typeof this.serverConfig?.port === 'undefined') {
321
+ return
322
+ }
323
+
327
324
  this.childManager = this.#createChildManager(
328
325
  loaderUrl,
329
326
  await this.getChildManagerContext(this.#basePath),
@@ -342,25 +339,20 @@ export class NextCapability extends BaseCapability {
342
339
  try {
343
340
  updateGlobals({ config: this.config })
344
341
  await this.childManager.inject()
342
+ await this.childManager.register()
345
343
  const { nextStart } = await importFile(resolvePath(this.#next, './dist/cli/next-start.js'))
346
344
 
347
- const { hostname, port, backlog } = this.serverConfig ?? {}
345
+ const { hostname, port } = this.serverConfig ?? {}
348
346
  const serverOptions = {
349
347
  hostname: hostname || '127.0.0.1',
350
348
  port: port || 0
351
349
  }
352
350
 
353
- await this.childManager.register()
354
-
355
- const serverPromise = createServerListener(
356
- (this.isEntrypoint ? serverOptions?.port : undefined) ?? true,
357
- (this.isEntrypoint ? serverOptions?.hostname : undefined) ?? true,
358
- typeof backlog === 'number' ? { backlog } : {}
359
- )
351
+ const serverPromise = createServerListener()
360
352
 
361
353
  if (this.#nextVersion.major === 14 && this.#nextVersion.minor < 2) {
362
354
  await nextStart({
363
- '--hostname': serverOptions.host,
355
+ '--hostname': serverOptions.hostname,
364
356
  '--port': serverOptions.port,
365
357
  _: [this.root]
366
358
  })
@@ -403,17 +395,13 @@ export class NextCapability extends BaseCapability {
403
395
  await this.childManager.inject()
404
396
  await this.childManager.register()
405
397
 
406
- const { hostname, port, backlog } = this.serverConfig ?? {}
398
+ const { hostname, port } = this.serverConfig ?? {}
407
399
  const serverOptions = {
408
400
  hostname: hostname || '127.0.0.1',
409
401
  port: port || 0
410
402
  }
411
403
 
412
- const serverPromise = createServerListener(
413
- (this.isEntrypoint ? serverOptions?.port : undefined) ?? true,
414
- (this.isEntrypoint ? serverOptions?.hostname : undefined) ?? true,
415
- typeof backlog === 'number' ? { backlog } : {}
416
- )
404
+ const serverPromise = createServerListener()
417
405
 
418
406
  let keepAliveTimeout = parseInt(process.env.KEEP_ALIVE_TIMEOUT, 10)
419
407
  if (Number.isNaN(keepAliveTimeout) || !Number.isFinite(keepAliveTimeout) || keepAliveTimeout < 0) {
@@ -448,6 +436,10 @@ export class NextCapability extends BaseCapability {
448
436
  scripts.push(new URL('./loader-next-15.cjs', import.meta.url))
449
437
  }
450
438
 
439
+ if (this.#nextVersion.major < 15 || (this.#nextVersion.major === 15 && this.#nextVersion.minor < 1)) {
440
+ scripts.push(new URL('./create-context-patch.js', import.meta.url))
441
+ }
442
+
451
443
  return scripts
452
444
  }
453
445
 
@@ -37,7 +37,16 @@ export async function packCommand (logger, configFile, args, context) {
37
37
 
38
38
  let config
39
39
  try {
40
- config = await loadConfiguration(configFile)
40
+ /*
41
+ A command receives the application's already-resolved configuration as data -- the loader
42
+ evaluated it once, main-side, and validated it against this capability's schema -- so
43
+ `resolved` says the reading is done. A path is still accepted, which is what a caller outside
44
+ a running project supplies, and it is loaded the way a boot loads one.
45
+ */
46
+ config =
47
+ typeof configFile === 'string'
48
+ ? await loadConfiguration(configFile)
49
+ : await loadConfiguration(context.application?.path ?? process.cwd(), configFile, { resolved: true })
41
50
  } catch (error) {
42
51
  return logFatalError(
43
52
  logger,
package/lib/config.js CHANGED
@@ -1,5 +1,4 @@
1
- import { transform as basicTransform, resolve, validationOptions } from '@platformatic/basic'
2
- import { loadConfiguration as utilsLoadConfiguration } from '@platformatic/foundation'
1
+ import { transform as basicTransform, loadCapabilityConfiguration, validationOptions } from '@platformatic/basic'
3
2
  import { schema } from './schema.js'
4
3
 
5
4
  export async function transform (config, schema, options) {
@@ -14,13 +13,11 @@ export async function transform (config, schema, options) {
14
13
  }
15
14
 
16
15
  export async function loadConfiguration (configOrRoot, sourceOrConfig, context) {
17
- const { root, source } = await resolve(configOrRoot, sourceOrConfig, 'application')
18
-
19
- return utilsLoadConfiguration(source, context?.schema ?? schema, {
16
+ return loadCapabilityConfiguration(configOrRoot, sourceOrConfig, context, {
17
+ schema,
18
+ scope: import.meta.filename,
19
+ suffixes: 'application',
20
20
  validationOptions,
21
- transform,
22
- replaceEnv: true,
23
- root,
24
- ...context
21
+ transform
25
22
  })
26
23
  }
@@ -1,7 +1,7 @@
1
1
  'use strict'
2
2
 
3
3
  import { createRequire } from 'node:module'
4
- import { getGlobalDispatcher } from 'undici'
4
+ import { fetch, getGlobalDispatcher } from 'undici'
5
5
 
6
6
  // Next.js runs middlewares in it's own patched vm context. So the global dispatcher in
7
7
  // the middleware context is different from an application global dispatcher. This
@@ -16,12 +16,16 @@ export function patchVmCreateContext () {
16
16
  const globalDispatcher = getGlobalDispatcher()
17
17
  const context = originalCreateContext(contextObject, opts)
18
18
  queueMicrotask(() => {
19
- if (contextObject.fetch === undefined) return
19
+ if (context.fetch === undefined) return
20
20
 
21
- const originalFetch = contextObject.fetch
22
- contextObject.fetch = (input, init = {}) => {
23
- init.dispatcher = globalDispatcher
24
- return originalFetch(input, init)
21
+ const originalFetch = context.fetch
22
+ context.fetch = (input, init = {}) => {
23
+ const url = typeof input === 'string' ? input : input?.url
24
+ if (!url?.startsWith('http://') && !url?.startsWith('https://')) {
25
+ return originalFetch(input, init)
26
+ }
27
+
28
+ return fetch(input, { ...init, dispatcher: globalDispatcher })
25
29
  }
26
30
  })
27
31
  return context
package/lib/factory.js ADDED
@@ -0,0 +1,7 @@
1
+ import { defineCapabilityFactory } from '@platformatic/basic'
2
+ import { schema, version } from './schema.js'
3
+
4
+ export const createNextConfig = defineCapabilityFactory('@platformatic/next', schema, {
5
+ version,
6
+ flatten: ['next']
7
+ })
@@ -1,14 +1,13 @@
1
1
  import {
2
2
  BaseCapability,
3
3
  buildListenOptions,
4
- errors as basicErrors,
5
4
  createServerListener,
5
+ errors as basicErrors,
6
6
  getServerUrl,
7
7
  importFile,
8
- resolvePackageViaCJS
8
+ resolvePackageViaCJS, cleanBasePath
9
9
  } from '@platformatic/basic'
10
- import { cleanBasePath } from '@platformatic/basic/lib/utils.js'
11
- import { ensureLoggableError } from '@platformatic/foundation/lib/errors.js'
10
+ import { ensureLoggableError } from '@platformatic/foundation'
12
11
  import { getEvents } from '@platformatic/globals'
13
12
  import { createQueue } from '@platformatic/image-optimizer'
14
13
  import { FileStorage, MemoryStorage, RedisStorage } from '@platformatic/job-queue'
@@ -72,37 +71,10 @@ export class NextImageOptimizerCapability extends BaseCapability {
72
71
  this.#queue = await this.#createQueue(imageOptimizerConfig)
73
72
  }
74
73
 
75
- async start ({ listen }) {
76
- // Make this idempotent
77
- if (this.url) {
78
- return this.url
79
- }
80
-
74
+ async _start () {
81
75
  const config = this.config
82
76
  this.#basePath = config.application?.basePath ? cleanBasePath(config.application?.basePath) : ''
83
- await super._start({ listen })
84
-
85
- if (this.#app && listen) {
86
- const serverOptions = this.serverConfig
87
- const listenOptions = buildListenOptions(serverOptions)
88
-
89
- if (typeof serverOptions?.backlog === 'number') {
90
- createServerListener(false, false, { backlog: serverOptions.backlog })
91
- listenOptions.backlog = serverOptions.backlog
92
- }
93
-
94
- this.#server = await new Promise((resolve, reject) => {
95
- return this.#app
96
- .listen(listenOptions, function () {
97
- resolve(this)
98
- })
99
- .on('error', reject)
100
- })
101
-
102
- this.url = getServerUrl(this.#server)
103
-
104
- return this.url
105
- }
77
+ await super._start()
106
78
 
107
79
  const { ImageOptimizerCache } = await importFile(resolvePath(this.#next, './dist/server/image-optimizer.js'))
108
80
  this.#validateParams = ImageOptimizerCache.validateParams.bind(ImageOptimizerCache)
@@ -118,10 +90,32 @@ export class NextImageOptimizerCapability extends BaseCapability {
118
90
  this.#app = createServer(this.#handleRequest.bind(this))
119
91
  this.#dispatcher = this.#app.listeners('request')[0]
120
92
  await this.#queue.start()
93
+
94
+ if (typeof this.serverConfig?.port === 'undefined') {
95
+ return
96
+ }
97
+
98
+ const serverOptions = this.serverConfig
99
+ const listenOptions = buildListenOptions(serverOptions)
100
+ const serverPromise = createServerListener()
101
+
102
+ const [server] = await Promise.all([
103
+ new Promise((resolve, reject) => {
104
+ return this.#app
105
+ .listen(listenOptions, function () {
106
+ resolve(this)
107
+ })
108
+ .on('error', reject)
109
+ }),
110
+ serverPromise
111
+ ])
112
+ this.#server = server
113
+
114
+ this.url = getServerUrl(this.#server)
121
115
  }
122
116
 
123
- async stop () {
124
- await super.stop()
117
+ async _stop () {
118
+ await super._stop()
125
119
  await this.#queue?.stop()
126
120
 
127
121
  const events = getEvents()
package/lib/pack.js CHANGED
@@ -15,7 +15,7 @@ import {
15
15
  symlink,
16
16
  writeFile
17
17
  } from 'node:fs/promises'
18
- import { createRequire } from 'node:module'
18
+ import { findPackageJSON } from 'node:module'
19
19
  import { tmpdir } from 'node:os'
20
20
  import { dirname, isAbsolute, relative, resolve as resolvePath } from 'node:path'
21
21
  import { version as platformaticVersion } from './schema.js'
@@ -169,60 +169,15 @@ async function loadPackageFromRoot (root, fallbackName) {
169
169
  }
170
170
 
171
171
  async function resolvePackageRoot (name, from) {
172
- const require = createRequire(from)
173
-
174
172
  try {
175
- const packageJsonPath = require.resolve(`${name}/package.json`)
176
- return dirname(packageJsonPath)
173
+ return dirname(findPackageJSON(name, from))
177
174
  } catch (error) {
178
- const packageJsonPath = resolvePackageJsonFromSearchPaths(require, name)
179
- if (packageJsonPath) {
180
- return dirname(packageJsonPath)
181
- }
182
-
183
- try {
184
- const resolvedEntry = require.resolve(name)
185
- return await findPackageRoot(dirname(resolvedEntry))
186
- } catch (resolveError) {
187
- const workspacePackage = resolveWorkspacePackageRoot(name)
188
- if (workspacePackage) {
189
- return workspacePackage
190
- }
191
-
192
- throw resolveError
193
- }
194
- }
195
- }
196
-
197
- function resolvePackageJsonFromSearchPaths (require, name) {
198
- const searchPaths = require.resolve.paths(name) ?? []
199
- const packageSegments = name.split('/')
200
-
201
- for (const searchPath of searchPaths) {
202
- const candidate = resolvePath(searchPath, ...packageSegments, 'package.json')
203
- if (existsSync(candidate)) {
204
- return candidate
205
- }
206
- }
207
-
208
- return null
209
- }
210
-
211
- async function findPackageRoot (directory) {
212
- let current = directory
213
-
214
- while (true) {
215
- const candidate = resolvePath(current, 'package.json')
216
- if (existsSync(candidate)) {
217
- return current
218
- }
219
-
220
- const parent = resolvePath(current, '..')
221
- if (parent === current) {
222
- throw new Error(`Cannot determine package root for ${directory}.`)
175
+ const workspacePackage = resolveWorkspacePackageRoot(name)
176
+ if (workspacePackage) {
177
+ return workspacePackage
223
178
  }
224
179
 
225
- current = parent
180
+ throw error
226
181
  }
227
182
  }
228
183
 
package/lib/schema.js CHANGED
@@ -1,11 +1,14 @@
1
- import { schemaComponents as basicSchemaComponents } from '@platformatic/basic'
2
- import { schemaComponents as utilsSchemaComponents } from '@platformatic/foundation'
1
+ import { schemaComponents as basicSchemaComponents } from '@platformatic/basic/schema'
2
+ import { schemaComponents as utilsSchemaComponents } from '@platformatic/foundation/schema'
3
3
  import { readFileSync } from 'node:fs'
4
4
  import { resolve } from 'node:path'
5
5
 
6
6
  export const packageJson = JSON.parse(readFileSync(resolve(import.meta.dirname, '../package.json'), 'utf8'))
7
7
  export const version = packageJson.version
8
8
 
9
+ // The port is checked before any startup path is selected, so no port means no start.
10
+ export const servesWithoutPort = { development: false, production: false }
11
+
9
12
  export const cache = {
10
13
  type: 'object',
11
14
  properties: {
@@ -240,6 +243,15 @@ const next = {
240
243
  additionalProperties: false
241
244
  }
242
245
 
246
+ /*
247
+ The names the generated types carry -- see the table in `foundation/lib/schema.js` for why they
248
+ are assigned rather than written at each site. `https` is Next's own, distinct from `server.https`
249
+ next to it, and the two names are what keeps that adjacency legible.
250
+ */
251
+ next.properties.https.title = 'NextHttpsOptions'
252
+ next.properties.imageOptimizer.title = 'ImageOptimizerOptions'
253
+ cache.title = 'NextCacheOptions'
254
+
243
255
  export const schemaComponents = { next }
244
256
 
245
257
  export const schema = {
package/package.json CHANGED
@@ -1,9 +1,15 @@
1
1
  {
2
2
  "name": "@platformatic/next",
3
- "version": "3.72.0",
3
+ "version": "4.0.0-alpha.1",
4
4
  "description": "Platformatic Next.js Capability",
5
5
  "main": "index.js",
6
6
  "type": "module",
7
+ "exports": {
8
+ ".": "./index.js",
9
+ "./schema": "./schema.js",
10
+ "./package.json": "./package.json",
11
+ "./*": "./*"
12
+ },
7
13
  "repository": {
8
14
  "type": "git",
9
15
  "url": "git+https://github.com/platformatic/platformatic.git"
@@ -19,21 +25,19 @@
19
25
  "@babel/parser": "^7.25.3",
20
26
  "@babel/traverse": "^7.25.3",
21
27
  "@babel/types": "^7.25.2",
22
- "@platformatic/basic": "3.72.0",
23
- "@platformatic/foundation": "3.72.0",
24
- "@platformatic/globals": "3.72.0",
25
28
  "@platformatic/image-optimizer": "^0.2.1",
26
29
  "amaro": "^1.0.0",
27
30
  "iovalkey": "^0.3.0",
28
31
  "light-my-request": "^6.0.0",
29
32
  "msgpackr": "^1.11.2",
30
- "semver": "^7.6.3"
33
+ "semver": "^7.6.3",
34
+ "@platformatic/foundation": "4.0.0-alpha.1",
35
+ "@platformatic/basic": "4.0.0-alpha.1",
36
+ "@platformatic/globals": "4.0.0-alpha.1"
31
37
  },
32
38
  "devDependencies": {
33
39
  "@fastify/reply-from": "^12.0.0",
34
- "@platformatic/gateway": "3.72.0",
35
- "@platformatic/service": "3.72.0",
36
- "@types/node": "^22.5.0",
40
+ "@types/node": "^24.0.0",
37
41
  "@types/react": "^19.1.11",
38
42
  "@types/react-dom": "^19.1.7",
39
43
  "cleaner-spec-reporter": "^0.5.0",
@@ -45,10 +49,12 @@
45
49
  "neostandard": "^0.12.0",
46
50
  "next": "^16.1.0",
47
51
  "typescript": "~6.0.3",
48
- "ws": "^8.18.0"
52
+ "ws": "^8.18.0",
53
+ "@platformatic/gateway": "4.0.0-alpha.1",
54
+ "@platformatic/service": "4.0.0-alpha.1"
49
55
  },
50
56
  "engines": {
51
- "node": ">=22.19.0"
57
+ "node": ">=24.20.0"
52
58
  },
53
59
  "scripts": {
54
60
  "test": "npm run test:types && npm run test:main && npm run test:caching && npm run test:compatibility && npm run test:integration",
@@ -56,6 +62,8 @@
56
62
  "test:main": "node --test --test-reporter=cleaner-spec-reporter --test-concurrency=1 --test-timeout=2000000 test/*.test.js",
57
63
  "test:caching": "node --test --test-reporter=cleaner-spec-reporter --test-concurrency=1 --test-timeout=2000000 test/caching/*.test.js",
58
64
  "test:compatibility": "node --test --test-reporter=cleaner-spec-reporter --test-concurrency=1 --test-timeout=2000000 test/compatibility/*.test.js",
65
+ "test:compatibility-development": "PLATFORMATIC_NEXT_COMPATIBILITY_MODE=development node --test --test-reporter=cleaner-spec-reporter --test-concurrency=1 --test-timeout=2000000 test/compatibility/*.test.js",
66
+ "test:compatibility-production": "PLATFORMATIC_NEXT_COMPATIBILITY_MODE=production node --test --test-reporter=cleaner-spec-reporter --test-concurrency=1 --test-timeout=2000000 test/compatibility/*.test.js",
59
67
  "test:integration": "node --test --test-reporter=cleaner-spec-reporter --test-concurrency=1 --test-timeout=2000000 test/integration/*.test.js",
60
68
  "gen-schema": "node lib/schema.js > schema.json",
61
69
  "gen-types": "json2ts > config.d.ts < schema.json",
package/schema.js ADDED
@@ -0,0 +1,7 @@
1
+ // The light /schema subpath, part of the capability contract. It carries the schema and the
2
+ // package-level metadata main-side preparation needs, and imports nothing that would pull the
3
+ // capability's implementation in with it — boot resolves this rather than the package entry.
4
+ //
5
+ // Light is a statement about import cost, never about safety: this executes in the main process
6
+ // with full privileges, like any capability code.
7
+ export * from './lib/schema.js'