@sanity/runtime-cli 17.1.0 → 17.2.0-blueprinit.g3a96975

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 (109) hide show
  1. package/README.md +38 -28
  2. package/SKILL.md +21 -0
  3. package/dist/actions/blueprinit/manifest.d.ts +13 -0
  4. package/dist/actions/blueprinit/manifest.js +57 -0
  5. package/dist/actions/blueprinit/studio.d.ts +6 -0
  6. package/dist/actions/blueprinit/studio.js +43 -0
  7. package/dist/actions/blueprinit/templates.d.ts +34 -0
  8. package/dist/actions/blueprinit/templates.js +83 -0
  9. package/dist/actions/blueprinit/workspace.d.ts +15 -0
  10. package/dist/actions/blueprinit/workspace.js +142 -0
  11. package/dist/actions/blueprints/assets.d.ts +2 -1
  12. package/dist/actions/blueprints/assets.js +17 -26
  13. package/dist/actions/blueprints/blueprint.d.ts +1 -1
  14. package/dist/actions/blueprints/pre-deploy.d.ts +29 -0
  15. package/dist/actions/blueprints/pre-deploy.js +164 -0
  16. package/dist/actions/blueprints/wait-for-operation.js +4 -0
  17. package/dist/actions/sanity/datasets.d.ts +15 -0
  18. package/dist/actions/sanity/datasets.js +26 -0
  19. package/dist/actions/sanity/examples.d.ts +4 -4
  20. package/dist/baseCommands.d.ts +11 -4
  21. package/dist/baseCommands.js +21 -0
  22. package/dist/commands/blueprinit.d.ts +14 -0
  23. package/dist/commands/blueprinit.js +45 -0
  24. package/dist/commands/blueprints/add.d.ts +1 -1
  25. package/dist/commands/blueprints/config.d.ts +1 -1
  26. package/dist/commands/blueprints/deploy.d.ts +1 -1
  27. package/dist/commands/blueprints/destroy.d.ts +1 -1
  28. package/dist/commands/blueprints/doctor.d.ts +0 -1
  29. package/dist/commands/blueprints/doctor.js +3 -7
  30. package/dist/commands/blueprints/info.d.ts +1 -1
  31. package/dist/commands/blueprints/logs.d.ts +1 -1
  32. package/dist/commands/blueprints/mint-deploy-token.d.ts +1 -1
  33. package/dist/commands/blueprints/plan.d.ts +1 -1
  34. package/dist/commands/blueprints/promote.d.ts +1 -1
  35. package/dist/commands/blueprints/stacks.d.ts +1 -1
  36. package/dist/commands/functions/add.d.ts +1 -1
  37. package/dist/commands/functions/build.d.ts +1 -1
  38. package/dist/commands/functions/dev.d.ts +1 -1
  39. package/dist/commands/functions/env/add.d.ts +4 -1
  40. package/dist/commands/functions/env/add.js +6 -2
  41. package/dist/commands/functions/env/list.d.ts +4 -1
  42. package/dist/commands/functions/env/list.js +9 -3
  43. package/dist/commands/functions/env/remove.d.ts +4 -1
  44. package/dist/commands/functions/env/remove.js +9 -3
  45. package/dist/commands/functions/logs.d.ts +1 -1
  46. package/dist/commands/functions/test.d.ts +1 -1
  47. package/dist/constants.d.ts +1 -0
  48. package/dist/constants.js +1 -0
  49. package/dist/cores/blueprinit/adopt.d.ts +6 -0
  50. package/dist/cores/blueprinit/adopt.js +90 -0
  51. package/dist/cores/blueprinit/finish.d.ts +14 -0
  52. package/dist/cores/blueprinit/finish.js +36 -0
  53. package/dist/cores/blueprinit/fresh.d.ts +3 -0
  54. package/dist/cores/blueprinit/fresh.js +88 -0
  55. package/dist/cores/blueprinit/prompts.d.ts +4 -0
  56. package/dist/cores/blueprinit/prompts.js +14 -0
  57. package/dist/cores/blueprinit/selection.d.ts +15 -0
  58. package/dist/cores/blueprinit/selection.js +63 -0
  59. package/dist/cores/blueprinit/types.d.ts +13 -0
  60. package/dist/cores/blueprinit/types.js +1 -0
  61. package/dist/cores/blueprinit.d.ts +7 -0
  62. package/dist/cores/blueprinit.js +39 -0
  63. package/dist/cores/blueprints/deploy.js +18 -65
  64. package/dist/cores/blueprints/doctor.js +15 -12
  65. package/dist/cores/functions/build.js +3 -2
  66. package/dist/cores/functions/test.js +4 -2
  67. package/dist/server/app.js +8 -2
  68. package/dist/server/static/favicon-dark.svg +1 -0
  69. package/dist/server/static/favicon.svg +1 -0
  70. package/dist/server/static/index.html +1 -1
  71. package/dist/server/static/vendor/vendor.bundle.js +5 -0
  72. package/dist/utils/command-skills.d.ts +2 -0
  73. package/dist/utils/command-skills.js +17 -0
  74. package/dist/utils/functions/child-process-wrapper.js +13 -171
  75. package/dist/utils/functions/child-runtime/console.d.ts +1 -0
  76. package/dist/utils/functions/child-runtime/console.js +50 -0
  77. package/dist/utils/functions/child-runtime/invoke.d.ts +4 -0
  78. package/dist/utils/functions/child-runtime/invoke.js +47 -0
  79. package/dist/utils/functions/child-runtime/resources.d.ts +4 -0
  80. package/dist/utils/functions/child-runtime/resources.js +59 -0
  81. package/dist/utils/functions/find-entry-point.d.ts +1 -1
  82. package/dist/utils/functions/find-entry-point.js +10 -12
  83. package/dist/utils/functions/invoke/local.js +1 -1
  84. package/dist/utils/functions/invoke/prepare.d.ts +1 -1
  85. package/dist/utils/functions/invoke/prepare.js +19 -8
  86. package/dist/utils/functions/package-json.d.ts +1 -2
  87. package/dist/utils/functions/package-json.js +4 -6
  88. package/dist/utils/functions/prepare-asset.d.ts +2 -2
  89. package/dist/utils/functions/prepare-asset.js +18 -7
  90. package/dist/utils/functions/resolve-dependencies.d.ts +4 -1
  91. package/dist/utils/functions/resolve-dependencies.js +5 -6
  92. package/dist/utils/functions/resource-to-arc.d.ts +1 -1
  93. package/dist/utils/functions/resource-to-arc.js +4 -5
  94. package/dist/utils/functions/should-transpile.d.ts +7 -1
  95. package/dist/utils/functions/should-transpile.js +10 -10
  96. package/dist/utils/functions/transpile/function.d.ts +4 -1
  97. package/dist/utils/functions/transpile/function.js +9 -14
  98. package/dist/utils/functions/transpile/verify-handler.js +1 -1
  99. package/dist/utils/logger.d.ts +7 -5
  100. package/dist/utils/logger.js +33 -1
  101. package/dist/utils/progress.d.ts +5 -0
  102. package/dist/utils/progress.js +40 -0
  103. package/dist/utils/studios/prepare-asset.d.ts +6 -0
  104. package/dist/utils/studios/prepare-asset.js +139 -0
  105. package/dist/utils/types.d.ts +9 -8
  106. package/dist/utils/types.js +2 -3
  107. package/oclif.manifest.json +1237 -930
  108. package/package.json +33 -24
  109. package/skills/blueprinit.md +137 -0
@@ -1,9 +1,13 @@
1
- import {table as renderTable} from 'node:console'
2
1
  import {existsSync, statSync} from 'node:fs'
3
2
  import {isAbsolute, join} from 'node:path'
4
3
  import process from 'node:process'
5
4
  import {pathToFileURL} from 'node:url'
6
- import {inspect} from 'node:util'
5
+ import {installConsole} from './child-runtime/console.js'
6
+ import {createInvoker} from './child-runtime/invoke.js'
7
+ import {createResourcesApi} from './child-runtime/resources.js'
8
+
9
+ installConsole()
10
+ const invoker = createInvoker()
7
11
 
8
12
  export function getFunctionSource(src) {
9
13
  const pathToCheck = isAbsolute(src) ? src : join(process.cwd(), src)
@@ -18,91 +22,6 @@ export function getFunctionSource(src) {
18
22
  return pathToFileURL(pathToCheck).href
19
23
  }
20
24
 
21
- // Monkey patch console menthods to have logs match server log format
22
- function logPrefix(level, ...args) {
23
- const date = new Date()
24
- const shouldUseColors = process.env.FORCE_COLOR === '1'
25
- const message = args
26
- .map((arg) =>
27
- typeof arg === 'string' ? arg : inspect(arg, {depth: null, colors: shouldUseColors}),
28
- )
29
- .join(' ')
30
- return `${date.toLocaleDateString()} ${date.toLocaleTimeString()} ${level.toUpperCase()} ${message}`
31
- }
32
-
33
- console.log = (...args) => {
34
- process.stdout.write(`${logPrefix('info', ...args)}\n`)
35
- }
36
-
37
- console.info = (...args) => {
38
- process.stdout.write(`${logPrefix('info', ...args)}\n`)
39
- }
40
-
41
- console.dir = (obj, options) => {
42
- const shouldUseColors = process.env.FORCE_COLOR === '1'
43
- const inspectOptions = {...options, colors: shouldUseColors}
44
- process.stdout.write(`${logPrefix('info', inspect(obj, inspectOptions))}\n`)
45
- }
46
-
47
- console.table = (data, columns) => {
48
- const prefix = logPrefix('info')
49
-
50
- // Capture table output
51
- const originalWrite = process.stdout.write.bind(process.stdout)
52
- let buffer = ''
53
-
54
- process.stdout.write = (chunk, _encoding, callback) => {
55
- buffer += chunk
56
- if (typeof callback === 'function') callback()
57
- }
58
-
59
- renderTable(data, columns)
60
-
61
- // Restore and print
62
- process.stdout.write = originalWrite
63
- for (const line of buffer.split('\n')) {
64
- if (line.trim()) {
65
- const message = `${!line.startsWith(prefix) ? `${prefix} ${line}` : line}`.trim()
66
- process.stdout.write(`${message}\n`)
67
- }
68
- }
69
- }
70
-
71
- console.warn = (...args) => {
72
- process.stdout.write(`${logPrefix('warn', ...args)}\n`)
73
- }
74
-
75
- console.error = (...args) => {
76
- process.stdout.write(`${logPrefix('error', ...args)}\n`)
77
- }
78
-
79
- /**
80
- * Pending nested `context.invoke` calls, keyed by correlation id. Each entry
81
- * holds the resolve/reject of the Promise returned to the user's handler; it is
82
- * settled when the matching `invoke-response` arrives from the parent.
83
- * @type {Map<string, {resolve: (value: unknown) => void, reject: (reason: Error) => void}>}
84
- */
85
- const pendingInvokes = new Map()
86
- let invokeSeq = 0
87
-
88
- /**
89
- * `context.invoke(name, payload)` — ask the parent to execute another blueprint
90
- * function and resolve with its return value. Sent over IPC and correlated by id.
91
- * @param {string} name
92
- * @param {Record<string, unknown>} [payload]
93
- * @returns {Promise<unknown>}
94
- */
95
- function invokeFunction(name, payload = {}) {
96
- if (typeof name !== 'string' || name.length === 0) {
97
- return Promise.reject(new Error('context.invoke requires a function name'))
98
- }
99
- const id = String(++invokeSeq)
100
- return new Promise((resolve, reject) => {
101
- pendingInvokes.set(id, {resolve, reject})
102
- process.send(JSON.stringify({type: 'invoke-request', id, name, payload}))
103
- })
104
- }
105
-
106
25
  // Listen for IPC messages from the parent process.
107
26
  process.on('message', async (data) => {
108
27
  let jsonData = null
@@ -115,18 +34,7 @@ process.on('message', async (data) => {
115
34
 
116
35
  // Response to a nested context.invoke() — route to the awaiting Promise and stop.
117
36
  if (jsonData?.type === 'invoke-response') {
118
- const pending = pendingInvokes.get(jsonData.id)
119
- if (!pending) return
120
- pendingInvokes.delete(jsonData.id)
121
- if (jsonData.ok) {
122
- pending.resolve(jsonData.result?.json)
123
- } else {
124
- const {message, stack, name} = jsonData.error ?? {}
125
- const error = new Error(message ?? 'Nested invoke failed')
126
- if (stack) error.stack = stack
127
- if (name) error.name = name
128
- pending.reject(error)
129
- }
37
+ invoker.handleResponse(jsonData)
130
38
  return
131
39
  }
132
40
 
@@ -160,25 +68,20 @@ process.on('message', async (data) => {
160
68
  try {
161
69
  // Import the function code
162
70
  const entry = await import(getFunctionSource(srcPath))
163
- const eventHandler = entry.handler || entry.default
164
-
165
- if (typeof eventHandler !== 'function') {
166
- throw new Error(
167
- 'No valid handler found. Please provide a default export or a named export, "handler"',
168
- )
71
+ const handler = entry.handler || entry.default
72
+ if (typeof handler !== 'function') {
73
+ throw new Error('No valid handler found. Provide a default export or a named "handler".')
169
74
  }
170
75
 
171
76
  // Replace resources JSON with Resources API
172
77
  context.resources = createResourcesApi(context.resources)
173
78
 
174
79
  // Allow the handler to invoke other blueprint functions by name.
175
- context.invoke = invokeFunction
176
-
177
- json = await eventHandler({
80
+ context.invoke = invoker.invoke
81
+ json = await handler({
178
82
  context,
179
83
  event,
180
- // mock sync-tag-invalidate done callback
181
- done: async (_tags) => new Response(null, {status: 204}),
84
+ done: async () => new Response(null, {status: 204}),
182
85
  })
183
86
 
184
87
  // Restore streams
@@ -191,7 +94,6 @@ process.on('message', async (data) => {
191
94
  // Restore streams on error
192
95
  process.stdout.write = originalStdoutWrite
193
96
  process.stderr.write = originalStderrWrite
194
-
195
97
  const errorInfo = {
196
98
  type: 'result',
197
99
  json: null,
@@ -207,63 +109,3 @@ process.on('message', async (data) => {
207
109
  process.exit(1)
208
110
  }
209
111
  })
210
-
211
- /**
212
- * The returned proxy wraps a function so the API itself is callable.
213
- * @param {Record<string, ResourceEntry[]>} resources
214
- * @returns {ResourcesApi}
215
- */
216
- export function createResourcesApi(resources) {
217
- const findByName = (name) => {
218
- for (const group of Object.values(resources)) {
219
- const found = group.find((r) => r.name === name)
220
- if (found) return found
221
- }
222
- return undefined
223
- }
224
- let allCache
225
- const all = () => (allCache ??= Object.values(resources).flat())
226
-
227
- return /** @type {ResourcesApi} */ (
228
- /** @type {unknown} */ (
229
- /*
230
- * The Proxy target is `findByName`, so invoking the API as a function —
231
- * `context.resources('my-proj')` — bypasses the `get` trap entirely and
232
- * calls `findByName` directly for a cross-type lookup by name.
233
- */
234
- new Proxy(findByName, {
235
- get: (_target, prop) => {
236
- // context.resources.all() — flat array of every resource across types
237
- if (prop === 'all') return all
238
- // [...context.resources] / for (const r of context.resources) — iterate all
239
- if (prop === Symbol.iterator) {
240
- return function* () {
241
- yield* all()
242
- }
243
- }
244
-
245
- /**
246
- * Symbol property access (other than iterator above) and `then`
247
- * lookups return undefined. The `then` guard prevents the API from
248
- * looking like a thenable, so `await context.resources` resolves to
249
- * the proxy itself rather than hanging or unwrapping unexpectedly.
250
- */
251
- if (typeof prop !== 'string' || prop === 'then') {
252
- return undefined
253
- }
254
-
255
- /**
256
- * context.resources.<type>(name) — per-type lookup,
257
- * e.g. context.resources.project('my-proj').
258
- * Returns a function so the caller supplies the name;
259
- * unknown types yield a function that always returns undefined.
260
- */
261
- return (/** @type {string} */ name) =>
262
- Object.hasOwn(resources, prop)
263
- ? resources[prop].find((r) => r.name === name)
264
- : undefined
265
- },
266
- })
267
- )
268
- )
269
- }
@@ -0,0 +1 @@
1
+ export function installConsole(): void
@@ -0,0 +1,50 @@
1
+ import {table as renderTable} from 'node:console'
2
+ import process from 'node:process'
3
+ import {inspect} from 'node:util'
4
+
5
+ // Monkey patch console methods to have logs match server log format
6
+ function logPrefix(level, ...args) {
7
+ const date = new Date()
8
+ const shouldUseColors = process.env.FORCE_COLOR === '1'
9
+ const message = args
10
+ .map((arg) =>
11
+ typeof arg === 'string' ? arg : inspect(arg, {depth: null, colors: shouldUseColors}),
12
+ )
13
+ .join(' ')
14
+ return `${date.toLocaleDateString()} ${date.toLocaleTimeString()} ${level.toUpperCase()} ${message}`
15
+ }
16
+
17
+ export function installConsole() {
18
+ console.log = (...args) => process.stdout.write(`${logPrefix('info', ...args)}\n`)
19
+ console.info = (...args) => process.stdout.write(`${logPrefix('info', ...args)}\n`)
20
+ console.warn = (...args) => process.stdout.write(`${logPrefix('warn', ...args)}\n`)
21
+ console.error = (...args) => process.stdout.write(`${logPrefix('error', ...args)}\n`)
22
+ console.dir = (obj, options) => {
23
+ const shouldUseColors = process.env.FORCE_COLOR === '1'
24
+ const inspectOptions = {...options, colors: shouldUseColors}
25
+ process.stdout.write(`${logPrefix('info', inspect(obj, inspectOptions))}\n`)
26
+ }
27
+ console.table = (data, columns) => {
28
+ const prefix = logPrefix('info')
29
+
30
+ // Capture table output
31
+ const originalWrite = process.stdout.write.bind(process.stdout)
32
+ let buffer = ''
33
+
34
+ process.stdout.write = (chunk, _encoding, callback) => {
35
+ buffer += chunk
36
+ if (typeof callback === 'function') callback()
37
+ }
38
+
39
+ renderTable(data, columns)
40
+
41
+ // Restore and print
42
+ process.stdout.write = originalWrite
43
+ for (const line of buffer.split('\n')) {
44
+ if (line.trim()) {
45
+ const message = `${!line.startsWith(prefix) ? `${prefix} ${line}` : line}`.trim()
46
+ process.stdout.write(`${message}\n`)
47
+ }
48
+ }
49
+ }
50
+ }
@@ -0,0 +1,4 @@
1
+ export function createInvoker(): {
2
+ invoke: (name: string, payload?: Record<string, unknown>) => Promise<unknown>
3
+ handleResponse: (msg: unknown) => void
4
+ }
@@ -0,0 +1,47 @@
1
+ import process from 'node:process'
2
+
3
+ export function createInvoker() {
4
+ /**
5
+ * Pending nested `context.invoke` calls, keyed by correlation id. Each entry
6
+ * holds the resolve/reject of the Promise returned to the user's handler; it is
7
+ * settled when the matching `invoke-response` arrives from the parent.
8
+ * @type {Map<string, {resolve: (value: unknown) => void, reject: (reason: Error) => void}>}
9
+ */
10
+ const pending = new Map()
11
+ let invokeSeq = 0
12
+
13
+ /**
14
+ * `context.invoke(name, payload)` — ask the parent to execute another blueprint
15
+ * function and resolve with its return value. Sent over IPC and correlated by id.
16
+ * @param {string} name
17
+ * @param {Record<string, unknown>} [payload]
18
+ * @returns {Promise<unknown>}
19
+ */
20
+ function invoke(name, payload = {}) {
21
+ if (typeof name !== 'string' || name.length === 0) {
22
+ return Promise.reject(new Error('context.invoke requires a function name'))
23
+ }
24
+ const id = String(++invokeSeq)
25
+ return new Promise((resolve, reject) => {
26
+ pending.set(id, {resolve, reject})
27
+ process.send(JSON.stringify({type: 'invoke-request', id, name, payload}))
28
+ })
29
+ }
30
+
31
+ function handleResponse(msg) {
32
+ const pendingEntry = pending.get(msg.id)
33
+ if (!pendingEntry) return
34
+ pending.delete(msg.id)
35
+ if (msg.ok) {
36
+ pendingEntry.resolve(msg.result?.json)
37
+ } else {
38
+ const {message, stack, name} = msg.error ?? {}
39
+ const err = new Error(message ?? 'Nested invoke failed')
40
+ if (stack) err.stack = stack
41
+ if (name) err.name = name
42
+ pendingEntry.reject(err)
43
+ }
44
+ }
45
+
46
+ return {invoke, handleResponse}
47
+ }
@@ -0,0 +1,4 @@
1
+ import type {ResourcesApi} from '@sanity/functions'
2
+ import type {GroupedBlueprintResources} from '../../blueprints/group-resources.js'
3
+
4
+ export function createResourcesApi(resources: GroupedBlueprintResources): ResourcesApi
@@ -0,0 +1,59 @@
1
+ /**
2
+ * The returned proxy wraps a function so the API itself is callable.
3
+ * @param {Record<string, ResourceEntry[]>} resources
4
+ * @returns {ResourcesApi}
5
+ */
6
+ export function createResourcesApi(resources) {
7
+ const findByName = (name) => {
8
+ for (const group of Object.values(resources)) {
9
+ const found = group.find((r) => r.name === name)
10
+ if (found) return found
11
+ }
12
+ return undefined
13
+ }
14
+ let allCache
15
+ const all = () => (allCache ??= Object.values(resources).flat())
16
+
17
+ return /** @type {ResourcesApi} */ (
18
+ /** @type {unknown} */ (
19
+ /*
20
+ * The Proxy target is `findByName`, so invoking the API as a function —
21
+ * `context.resources('my-proj')` — bypasses the `get` trap entirely and
22
+ * calls `findByName` directly for a cross-type lookup by name.
23
+ */
24
+ new Proxy(findByName, {
25
+ get: (_target, prop) => {
26
+ // context.resources.all() — flat array of every resource across types
27
+ if (prop === 'all') return all
28
+ // [...context.resources] / for (const r of context.resources) — iterate all
29
+ if (prop === Symbol.iterator) {
30
+ return function* () {
31
+ yield* all()
32
+ }
33
+ }
34
+
35
+ /**
36
+ * Symbol property access (other than iterator above) and `then`
37
+ * lookups return undefined. The `then` guard prevents the API from
38
+ * looking like a thenable, so `await context.resources` resolves to
39
+ * the proxy itself rather than hanging or unwrapping unexpectedly.
40
+ */
41
+ if (typeof prop !== 'string' || prop === 'then') {
42
+ return undefined
43
+ }
44
+
45
+ /**
46
+ * context.resources.<type>(name) — per-type lookup,
47
+ * e.g. context.resources.project('my-proj').
48
+ * Returns a function so the caller supplies the name;
49
+ * unknown types yield a function that always returns undefined.
50
+ */
51
+ return (/** @type {string} */ name) =>
52
+ Object.hasOwn(resources, prop)
53
+ ? resources[prop].find((r) => r.name === name)
54
+ : undefined
55
+ },
56
+ })
57
+ )
58
+ )
59
+ }
@@ -8,4 +8,4 @@
8
8
  * @returns The absolute path to the entry file.
9
9
  * @throws If the entry file cannot be determined.
10
10
  */
11
- export declare function findFunctionEntryPoint(srcPath: string, displayName?: string): Promise<string>;
11
+ export declare function findFunctionEntryPoint(functionPath: string, displayName?: string): Promise<string>;
@@ -1,6 +1,5 @@
1
1
  import { readFile, stat } from 'node:fs/promises';
2
2
  import { join, resolve } from 'node:path';
3
- import { cwd } from 'node:process';
4
3
  /**
5
4
  * Resolves the source path to an executable entry file path.
6
5
  *
@@ -11,27 +10,26 @@ import { cwd } from 'node:process';
11
10
  * @returns The absolute path to the entry file.
12
11
  * @throws If the entry file cannot be determined.
13
12
  */
14
- export async function findFunctionEntryPoint(srcPath, displayName) {
15
- const absolutePath = resolve(cwd(), srcPath);
13
+ export async function findFunctionEntryPoint(functionPath, displayName) {
16
14
  let stats;
17
15
  try {
18
- stats = await stat(absolutePath);
16
+ stats = await stat(functionPath);
19
17
  }
20
18
  catch (err) {
21
- throw new Error(`Source path not found or inaccessible: ${srcPath}`, { cause: err });
19
+ throw new Error(`Source path not found or inaccessible: ${functionPath}`, { cause: err });
22
20
  }
23
21
  if (stats.isFile()) {
24
22
  // It's already an entry file path
25
- return absolutePath;
23
+ return functionPath;
26
24
  }
27
25
  if (stats.isDirectory()) {
28
26
  // 1. Check package.json#main
29
27
  try {
30
- const pkgJsonPath = join(absolutePath, 'package.json');
28
+ const pkgJsonPath = join(functionPath, 'package.json');
31
29
  const pkgJsonContent = await readFile(pkgJsonPath, 'utf8');
32
30
  const pkgJson = JSON.parse(pkgJsonContent);
33
31
  if (pkgJson.main) {
34
- const mainPath = resolve(absolutePath, pkgJson.main);
32
+ const mainPath = resolve(functionPath, pkgJson.main);
35
33
  if (await fileExists(mainPath)) {
36
34
  return mainPath;
37
35
  }
@@ -43,20 +41,20 @@ export async function findFunctionEntryPoint(srcPath, displayName) {
43
41
  // Consider warning the user on invalid JSON though?
44
42
  }
45
43
  // 2. Check index.ts
46
- const indexTs = join(absolutePath, 'index.ts');
44
+ const indexTs = join(functionPath, 'index.ts');
47
45
  if (await fileExists(indexTs)) {
48
46
  return indexTs;
49
47
  }
50
48
  // 3. Check index.js
51
- const indexJs = join(absolutePath, 'index.js');
49
+ const indexJs = join(functionPath, 'index.js');
52
50
  if (await fileExists(indexJs)) {
53
51
  return indexJs;
54
52
  }
55
53
  const nameHint = displayName ? ` for function "${displayName}"` : '';
56
- throw new Error(`Could not determine entry file${nameHint} in directory: ${srcPath}. Looked for package.json#main, index.ts, index.js.`);
54
+ throw new Error(`Could not determine entry file${nameHint} in directory: ${functionPath}. Looked for package.json#main, index.ts, index.js.`);
57
55
  }
58
56
  // Should not happen if stat succeeded, but defensively handle
59
- throw new Error(`Source path is neither a file nor a directory: ${srcPath}`);
57
+ throw new Error(`Source path is neither a file nor a directory: ${functionPath}`);
60
58
  }
61
59
  /**
62
60
  * Checks if a file exists and is a file.
@@ -66,7 +66,7 @@ export default async function invoke(resource, payload, context, options) {
66
66
  // Acquire a shared, reference-counted build. Concurrent invokes of the same
67
67
  // function reuse one transpile + temp package.json instead of racing on the
68
68
  // per-function build paths; the build is torn down once every invoke releases.
69
- const build = acquireFunctionBuild(resource);
69
+ const build = acquireFunctionBuild(resource, context.blueprintFileDir);
70
70
  let functionPath;
71
71
  let bundleTimings;
72
72
  try {
@@ -17,4 +17,4 @@ export interface FunctionBuild {
17
17
  * caller MUST call `release()` exactly once when its child has exited, so the
18
18
  * shared artifacts are torn down only after the last in-flight invoke finishes.
19
19
  */
20
- export declare function acquireFunctionBuild(resource: FunctionResource): FunctionBuild;
20
+ export declare function acquireFunctionBuild(resource: FunctionResource, blueprintFileDir: string): FunctionBuild;
@@ -1,3 +1,4 @@
1
+ import path from 'node:path';
1
2
  import { findFunctionEntryPoint } from '../find-entry-point.js';
2
3
  import { cleanupTempPackageJson, createTempPackageJson, doesPackageJsonExists, } from '../package-json.js';
3
4
  import { resolveResourceDependencies } from '../resolve-dependencies.js';
@@ -22,34 +23,44 @@ function buildKey(resource) {
22
23
  * caller MUST call `release()` exactly once when its child has exited, so the
23
24
  * shared artifacts are torn down only after the last in-flight invoke finishes.
24
25
  */
25
- export function acquireFunctionBuild(resource) {
26
+ export function acquireFunctionBuild(resource, blueprintFileDir) {
26
27
  const key = buildKey(resource);
27
28
  let entry = builds.get(key);
28
29
  if (!entry) {
29
30
  let cleanupBundle = async () => { };
30
- let functionPath = '';
31
+ const resolvedFunctionPath = path.resolve(blueprintFileDir, resource.src);
32
+ let functionPath = resolvedFunctionPath;
31
33
  let createdTempPackageJson = false;
32
34
  // If no package.json exists we create a temporary one to avoid the
33
35
  // "module type" warning from node.
34
- const existingPackageJson = doesPackageJsonExists(resource);
36
+ const existingPackageJson = doesPackageJsonExists(functionPath);
35
37
  const ready = (async () => {
36
38
  if (!resource.src) {
37
39
  throw new Error(`Function resource "${resource.name}" is missing the 'src' property.`);
38
40
  }
39
41
  let bundleTimings;
40
- const shouldTranspile = await shouldTranspileFunction(resource);
41
- if (shouldTranspile) {
42
- const transpileResult = await transpileFunction(resource);
42
+ const shouldTranspile = await shouldTranspileFunction(resource, functionPath);
43
+ if (shouldTranspile.transpile) {
44
+ const transpileResult = await transpileFunction({
45
+ resource,
46
+ functionPath,
47
+ entryPoint: shouldTranspile.entryPoint,
48
+ });
43
49
  functionPath = await findFunctionEntryPoint(transpileResult.outputDir);
44
50
  bundleTimings = transpileResult.timings;
45
51
  cleanupBundle = transpileResult.cleanup;
46
52
  }
47
53
  else {
48
- functionPath = await findFunctionEntryPoint(resource.src, resource.displayName ?? resource.name);
54
+ functionPath = await findFunctionEntryPoint(functionPath, resource.displayName ?? resource.name);
49
55
  }
50
56
  const shouldResolveDependencies = await shouldAutoResolveDependencies(resource);
51
57
  if (shouldResolveDependencies && existingPackageJson) {
52
- await resolveResourceDependencies(resource, { transpiled: shouldTranspile });
58
+ await resolveResourceDependencies({
59
+ resource,
60
+ functionPath: resolvedFunctionPath,
61
+ blueprintFileDir,
62
+ transpiled: shouldTranspile.transpile,
63
+ });
53
64
  }
54
65
  if (!existingPackageJson) {
55
66
  createTempPackageJson(functionPath);
@@ -1,4 +1,3 @@
1
- import type { FunctionResource } from '../types.js';
2
- export declare function doesPackageJsonExists(resource: FunctionResource): boolean;
1
+ export declare function doesPackageJsonExists(functionPath: string): boolean;
3
2
  export declare function createTempPackageJson(functionPath: string): boolean;
4
3
  export declare function cleanupTempPackageJson(functionPath: string): boolean;
@@ -1,17 +1,15 @@
1
1
  import { existsSync, rmSync, writeFileSync } from 'node:fs';
2
- import { dirname, join, resolve } from 'node:path';
3
- import { cwd } from 'node:process';
2
+ import { dirname, join } from 'node:path';
4
3
  function getPackageJsonPath(functionPath) {
5
4
  const functionDir = dirname(functionPath);
6
5
  return join(functionDir, 'package.json');
7
6
  }
8
- export function doesPackageJsonExists(resource) {
9
- if (!resource.src) {
7
+ export function doesPackageJsonExists(functionPath) {
8
+ if (!functionPath) {
10
9
  // Cannot determine without a source path
11
10
  return false;
12
11
  }
13
- const absolutePath = resolve(cwd(), resource.src);
14
- const packageJsonPath = join(absolutePath, 'package.json');
12
+ const packageJsonPath = join(functionPath, 'package.json');
15
13
  return existsSync(packageJsonPath);
16
14
  }
17
15
  export function createTempPackageJson(functionPath) {
@@ -1,6 +1,6 @@
1
1
  import type { AssetPrepResult, FunctionResource, InstallerType } from '../types.js';
2
- export declare function prepareAsset({ resource, }: {
2
+ export declare function prepareAsset({ blueprintFileDir, resource, installer, }: {
3
+ blueprintFileDir: string;
3
4
  resource: FunctionResource;
4
- }, { installer }?: {
5
5
  installer?: InstallerType;
6
6
  }): Promise<AssetPrepResult>;
@@ -1,7 +1,6 @@
1
1
  import { existsSync } from 'node:fs';
2
2
  import { rm, stat } from 'node:fs/promises';
3
3
  import path from 'node:path';
4
- import { cwd } from 'node:process';
5
4
  import { MAX_ASSET_SIZE } from '../../constants.js';
6
5
  import { detectNativeModules, errorMessage } from './detect-native-modules.js';
7
6
  import { getFolderSize } from './get-folder-size.js';
@@ -9,16 +8,22 @@ import { resolveResourceDependencies } from './resolve-dependencies.js';
9
8
  import { shouldAutoResolveDependencies } from './should-auto-resolve-deps.js';
10
9
  import { shouldTranspileFunction } from './should-transpile.js';
11
10
  import { transpileFunction } from './transpile/function.js';
12
- export async function prepareAsset({ resource, }, { installer } = {}) {
11
+ export async function prepareAsset({ blueprintFileDir, resource, installer, }) {
13
12
  if (!resource.src)
14
13
  throw new Error('Resource src is required');
15
- let functionPath = path.join(cwd(), resource.src);
14
+ const resolvedFunctionPath = path.resolve(blueprintFileDir, resource.src);
15
+ let functionPath = resolvedFunctionPath;
16
16
  let cleanup = async () => { };
17
17
  let wasBundled = false;
18
- const shouldTranspile = await shouldTranspileFunction(resource);
19
- if (shouldTranspile) {
18
+ const shouldTranspile = await shouldTranspileFunction(resource, functionPath);
19
+ if (shouldTranspile.transpile) {
20
20
  try {
21
- const result = await transpileFunction(resource, { installer });
21
+ const result = await transpileFunction({
22
+ resource,
23
+ functionPath,
24
+ entryPoint: shouldTranspile.entryPoint,
25
+ installer,
26
+ });
22
27
  functionPath = result.outputDir;
23
28
  cleanup = result.cleanup;
24
29
  wasBundled = result.bundled;
@@ -29,7 +34,13 @@ export async function prepareAsset({ resource, }, { installer } = {}) {
29
34
  }
30
35
  const shouldResolveDependencies = await shouldAutoResolveDependencies(resource);
31
36
  if (shouldResolveDependencies && !wasBundled) {
32
- await resolveResourceDependencies(resource, { transpiled: shouldTranspile, installer });
37
+ await resolveResourceDependencies({
38
+ resource,
39
+ functionPath: resolvedFunctionPath,
40
+ blueprintFileDir,
41
+ transpiled: shouldTranspile.transpile,
42
+ installer,
43
+ });
33
44
  await removeArcAutoInstallPackageJson(functionPath);
34
45
  }
35
46
  if (existsSync(path.join(functionPath, 'node_modules'))) {
@@ -1,5 +1,8 @@
1
1
  import type { FunctionResource, InstallerType } from '../types.js';
2
- export declare function resolveResourceDependencies(resource: FunctionResource, { transpiled, installer, }: {
2
+ export declare function resolveResourceDependencies({ resource, transpiled, blueprintFileDir, functionPath, installer, }: {
3
+ resource: FunctionResource;
4
+ blueprintFileDir: string;
5
+ functionPath: string;
3
6
  transpiled: boolean;
4
7
  installer?: InstallerType;
5
8
  }): Promise<void>;