@hagicode/hagiscript 0.2.8-dev.128.1.2ae1976 → 0.2.8
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/package.json +2 -2
- package/runtime/lib/runtime-script-lib.mjs +263 -60
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hagicode/hagiscript",
|
|
3
|
-
"version": "0.2.8
|
|
3
|
+
"version": "0.2.8",
|
|
4
4
|
"description": "Scoped npm package foundation for Hagiscript language tooling.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"homepage": "https://github.com/HagiCode-org/hagiscript#readme",
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"format:check": "prettier --check .",
|
|
38
38
|
"lint": "eslint .",
|
|
39
39
|
"integration:installed-runtime": "node scripts/integration-installed-runtime.mjs",
|
|
40
|
-
"integration:runtime-key-path": "node scripts/integration-runtime-key-path.mjs",
|
|
40
|
+
"integration:runtime-key-path": "npm run build && node scripts/integration-runtime-key-path.mjs",
|
|
41
41
|
"integration:runtime-management": "npm run build && node scripts/integration-runtime-management.mjs",
|
|
42
42
|
"playground:manifest:generate": "node scripts/generate-playground-manifest.mjs",
|
|
43
43
|
"playground:runtime:install": "npm run playground:manifest:generate && npm run dev -- runtime install --from-manifest ./playground/generated/manifest.yaml --runtime-root ./playground/runtime-root",
|
|
@@ -4,6 +4,7 @@ import { access, chmod, cp, mkdir, mkdtemp, readFile, readdir, rename, rm, stat,
|
|
|
4
4
|
import { homedir, tmpdir } from "node:os"
|
|
5
5
|
import path from "node:path"
|
|
6
6
|
import process from "node:process"
|
|
7
|
+
import { URL } from "node:url"
|
|
7
8
|
import { createGunzip } from "node:zlib"
|
|
8
9
|
import { pipeline } from "node:stream/promises"
|
|
9
10
|
import { extractZipArchive as extractZipArchiveWithNode } from "./zip-extract.mjs"
|
|
@@ -188,31 +189,26 @@ export async function installVendoredPackage(context, options) {
|
|
|
188
189
|
context.vendoredRepository,
|
|
189
190
|
context.vendoredBaseUrl
|
|
190
191
|
)
|
|
191
|
-
const releaseTag = context.vendoredTag
|
|
192
192
|
const platform = normalizeVendoredPlatform(process.platform)
|
|
193
193
|
const arch = normalizeVendoredArchitecture(process.arch)
|
|
194
194
|
const installMode = options.installMode || context.bundledInstallMode || "extract"
|
|
195
195
|
const archiveFormat = resolveVendoredArchiveFormat(platform, installMode)
|
|
196
|
-
const
|
|
196
|
+
const preferredSource = createVendoredAssetSource({
|
|
197
|
+
repository,
|
|
198
|
+
baseUrl,
|
|
199
|
+
releaseTag: context.vendoredTag,
|
|
197
200
|
packageName: options.packageName,
|
|
198
|
-
releaseTag,
|
|
199
201
|
platform,
|
|
200
202
|
arch,
|
|
201
|
-
archiveFormat
|
|
203
|
+
archiveFormat,
|
|
204
|
+
downloadCacheDir: context.downloadCacheDir
|
|
202
205
|
})
|
|
203
|
-
const assetUrl = buildVendoredAssetUrl(baseUrl, repository, releaseTag, assetName)
|
|
204
|
-
const cacheRoot = path.join(
|
|
205
|
-
context.downloadCacheDir,
|
|
206
|
-
"vendored",
|
|
207
|
-
options.packageName,
|
|
208
|
-
releaseTag,
|
|
209
|
-
`${platform}-${arch}`
|
|
210
|
-
)
|
|
211
206
|
|
|
212
207
|
if (installMode === "archive-7z-only") {
|
|
213
208
|
const archivePath =
|
|
214
|
-
options.archivePath || path.join(path.dirname(options.prefixRoot), "archives", assetName)
|
|
215
|
-
|
|
209
|
+
options.archivePath || path.join(path.dirname(options.prefixRoot), "archives", preferredSource.assetName)
|
|
210
|
+
let resolvedSource = preferredSource
|
|
211
|
+
let archiveCachePath = path.join(resolvedSource.cacheRoot, resolvedSource.assetName)
|
|
216
212
|
|
|
217
213
|
if (context.downloadCacheEnabled) {
|
|
218
214
|
const restoredArchivePath = await restoreVendoredArchiveFromCache(
|
|
@@ -220,91 +216,134 @@ export async function installVendoredPackage(context, options) {
|
|
|
220
216
|
archivePath
|
|
221
217
|
)
|
|
222
218
|
if (restoredArchivePath) {
|
|
223
|
-
return {
|
|
219
|
+
return buildVendoredInstallResult(resolvedSource, {
|
|
224
220
|
installMode,
|
|
225
221
|
archivePath: restoredArchivePath,
|
|
226
|
-
archiveFormat
|
|
227
|
-
|
|
228
|
-
releaseTag,
|
|
229
|
-
releaseName: releaseTag.replace(/^v/u, ""),
|
|
230
|
-
releaseUrl: `${baseUrl}/${repository}/releases/tag/${encodeURIComponent(releaseTag)}`,
|
|
231
|
-
releaseAssetName: assetName,
|
|
232
|
-
releaseAssetUrl: assetUrl
|
|
233
|
-
}
|
|
222
|
+
archiveFormat
|
|
223
|
+
})
|
|
234
224
|
}
|
|
235
225
|
}
|
|
236
226
|
|
|
237
227
|
await ensureDirectory(path.dirname(archivePath))
|
|
238
|
-
|
|
228
|
+
|
|
229
|
+
try {
|
|
230
|
+
await downloadVendoredAsset(resolvedSource.assetUrl, archivePath)
|
|
231
|
+
} catch (error) {
|
|
232
|
+
const fallbackSource = await resolveFallbackVendoredAssetSource(
|
|
233
|
+
preferredSource,
|
|
234
|
+
error
|
|
235
|
+
)
|
|
236
|
+
|
|
237
|
+
if (!fallbackSource) {
|
|
238
|
+
throw error
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
resolvedSource = fallbackSource
|
|
242
|
+
archiveCachePath = path.join(resolvedSource.cacheRoot, resolvedSource.assetName)
|
|
243
|
+
|
|
244
|
+
if (context.downloadCacheEnabled) {
|
|
245
|
+
const restoredArchivePath = await restoreVendoredArchiveFromCache(
|
|
246
|
+
archiveCachePath,
|
|
247
|
+
archivePath
|
|
248
|
+
)
|
|
249
|
+
if (restoredArchivePath) {
|
|
250
|
+
return buildVendoredInstallResult(resolvedSource, {
|
|
251
|
+
installMode,
|
|
252
|
+
archivePath: restoredArchivePath,
|
|
253
|
+
archiveFormat
|
|
254
|
+
})
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
await downloadVendoredAsset(resolvedSource.assetUrl, archivePath)
|
|
259
|
+
}
|
|
239
260
|
|
|
240
261
|
if (context.downloadCacheEnabled) {
|
|
241
262
|
await storeFileInCache(archivePath, archiveCachePath)
|
|
242
263
|
}
|
|
243
264
|
|
|
244
|
-
return {
|
|
265
|
+
return buildVendoredInstallResult(resolvedSource, {
|
|
245
266
|
installMode,
|
|
246
267
|
archivePath,
|
|
247
|
-
archiveFormat
|
|
248
|
-
|
|
249
|
-
releaseTag,
|
|
250
|
-
releaseName: releaseTag.replace(/^v/u, ""),
|
|
251
|
-
releaseUrl: `${baseUrl}/${repository}/releases/tag/${encodeURIComponent(releaseTag)}`,
|
|
252
|
-
releaseAssetName: assetName,
|
|
253
|
-
releaseAssetUrl: assetUrl
|
|
254
|
-
}
|
|
268
|
+
archiveFormat
|
|
269
|
+
})
|
|
255
270
|
}
|
|
256
271
|
|
|
257
272
|
if (context.downloadCacheEnabled) {
|
|
258
273
|
const restoredEntrypoint = await restoreVendoredPackageFromCache(
|
|
259
|
-
cacheRoot,
|
|
274
|
+
preferredSource.cacheRoot,
|
|
260
275
|
options.prefixRoot,
|
|
261
276
|
options.entrypointRelativePath
|
|
262
277
|
)
|
|
263
278
|
if (restoredEntrypoint) {
|
|
264
|
-
return {
|
|
279
|
+
return buildVendoredInstallResult(preferredSource, {
|
|
265
280
|
installMode,
|
|
266
281
|
entrypointPath: restoredEntrypoint,
|
|
267
|
-
archiveFormat
|
|
268
|
-
|
|
269
|
-
releaseTag,
|
|
270
|
-
releaseName: releaseTag.replace(/^v/u, ""),
|
|
271
|
-
releaseUrl: `${baseUrl}/${repository}/releases/tag/${encodeURIComponent(releaseTag)}`,
|
|
272
|
-
releaseAssetName: assetName,
|
|
273
|
-
releaseAssetUrl: assetUrl
|
|
274
|
-
}
|
|
282
|
+
archiveFormat
|
|
283
|
+
})
|
|
275
284
|
}
|
|
276
285
|
}
|
|
277
286
|
|
|
278
287
|
const stagingRoot = await mkdtemp(path.join(tmpdir(), `hagiscript-vendored-${options.packageName}-`))
|
|
279
288
|
|
|
280
289
|
try {
|
|
281
|
-
const archivePath = path.join(stagingRoot, assetName)
|
|
282
290
|
const extractRoot = path.join(stagingRoot, "extract")
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
291
|
+
let resolvedSource = preferredSource
|
|
292
|
+
let extractedRoot
|
|
293
|
+
|
|
294
|
+
try {
|
|
295
|
+
extractedRoot = await downloadAndExtractVendoredPackage(
|
|
296
|
+
resolvedSource,
|
|
297
|
+
stagingRoot,
|
|
298
|
+
extractRoot
|
|
299
|
+
)
|
|
300
|
+
} catch (error) {
|
|
301
|
+
const fallbackSource = await resolveFallbackVendoredAssetSource(
|
|
302
|
+
preferredSource,
|
|
303
|
+
error
|
|
304
|
+
)
|
|
305
|
+
|
|
306
|
+
if (!fallbackSource) {
|
|
307
|
+
throw error
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
resolvedSource = fallbackSource
|
|
311
|
+
|
|
312
|
+
if (context.downloadCacheEnabled) {
|
|
313
|
+
const restoredEntrypoint = await restoreVendoredPackageFromCache(
|
|
314
|
+
resolvedSource.cacheRoot,
|
|
315
|
+
options.prefixRoot,
|
|
316
|
+
options.entrypointRelativePath
|
|
317
|
+
)
|
|
318
|
+
if (restoredEntrypoint) {
|
|
319
|
+
return buildVendoredInstallResult(resolvedSource, {
|
|
320
|
+
installMode,
|
|
321
|
+
entrypointPath: restoredEntrypoint,
|
|
322
|
+
archiveFormat
|
|
323
|
+
})
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
extractedRoot = await downloadAndExtractVendoredPackage(
|
|
328
|
+
resolvedSource,
|
|
329
|
+
stagingRoot,
|
|
330
|
+
extractRoot
|
|
331
|
+
)
|
|
332
|
+
}
|
|
333
|
+
|
|
289
334
|
await replaceDirectory(extractedRoot, options.prefixRoot)
|
|
290
335
|
if (context.downloadCacheEnabled) {
|
|
291
|
-
await storeDirectoryInCache(options.prefixRoot, cacheRoot)
|
|
336
|
+
await storeDirectoryInCache(options.prefixRoot, resolvedSource.cacheRoot)
|
|
292
337
|
}
|
|
293
338
|
|
|
294
339
|
const entrypointPath = path.join(options.prefixRoot, options.entrypointRelativePath)
|
|
295
340
|
await access(entrypointPath)
|
|
296
341
|
|
|
297
|
-
return {
|
|
342
|
+
return buildVendoredInstallResult(resolvedSource, {
|
|
298
343
|
installMode,
|
|
299
344
|
entrypointPath,
|
|
300
|
-
archiveFormat
|
|
301
|
-
|
|
302
|
-
releaseTag,
|
|
303
|
-
releaseName: releaseTag.replace(/^v/u, ""),
|
|
304
|
-
releaseUrl: `${baseUrl}/${repository}/releases/tag/${encodeURIComponent(releaseTag)}`,
|
|
305
|
-
releaseAssetName: assetName,
|
|
306
|
-
releaseAssetUrl: assetUrl
|
|
307
|
-
}
|
|
345
|
+
archiveFormat
|
|
346
|
+
})
|
|
308
347
|
} finally {
|
|
309
348
|
await rm(stagingRoot, { recursive: true, force: true })
|
|
310
349
|
}
|
|
@@ -469,6 +508,63 @@ function buildVendoredAssetUrl(baseUrl, repository, releaseTag, assetName) {
|
|
|
469
508
|
return `${baseUrl}/${repository}/releases/download/${encodeURIComponent(releaseTag)}/${encodeURIComponent(assetName)}`
|
|
470
509
|
}
|
|
471
510
|
|
|
511
|
+
function buildVendoredReleaseUrl(baseUrl, repository, releaseTag) {
|
|
512
|
+
return `${baseUrl}/${repository}/releases/tag/${encodeURIComponent(releaseTag)}`
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
function createVendoredAssetSource(options) {
|
|
516
|
+
const assetName = buildVendoredAssetName({
|
|
517
|
+
packageName: options.packageName,
|
|
518
|
+
releaseTag: options.releaseTag,
|
|
519
|
+
platform: options.platform,
|
|
520
|
+
arch: options.arch,
|
|
521
|
+
archiveFormat: options.archiveFormat
|
|
522
|
+
})
|
|
523
|
+
|
|
524
|
+
return {
|
|
525
|
+
repository: options.repository,
|
|
526
|
+
baseUrl: options.baseUrl,
|
|
527
|
+
releaseTag: options.releaseTag,
|
|
528
|
+
releaseName: options.releaseTag.replace(/^v/u, ""),
|
|
529
|
+
releaseUrl: buildVendoredReleaseUrl(
|
|
530
|
+
options.baseUrl,
|
|
531
|
+
options.repository,
|
|
532
|
+
options.releaseTag
|
|
533
|
+
),
|
|
534
|
+
packageName: options.packageName,
|
|
535
|
+
platform: options.platform,
|
|
536
|
+
arch: options.arch,
|
|
537
|
+
archiveFormat: options.archiveFormat,
|
|
538
|
+
assetName,
|
|
539
|
+
assetUrl: buildVendoredAssetUrl(
|
|
540
|
+
options.baseUrl,
|
|
541
|
+
options.repository,
|
|
542
|
+
options.releaseTag,
|
|
543
|
+
assetName
|
|
544
|
+
),
|
|
545
|
+
downloadCacheDir: options.downloadCacheDir,
|
|
546
|
+
cacheRoot: path.join(
|
|
547
|
+
options.downloadCacheDir,
|
|
548
|
+
"vendored",
|
|
549
|
+
options.packageName,
|
|
550
|
+
options.releaseTag,
|
|
551
|
+
`${options.platform}-${options.arch}`
|
|
552
|
+
)
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
function buildVendoredInstallResult(source, extra = {}) {
|
|
557
|
+
return {
|
|
558
|
+
releaseRepository: source.repository,
|
|
559
|
+
releaseTag: source.releaseTag,
|
|
560
|
+
releaseName: source.releaseName,
|
|
561
|
+
releaseUrl: source.releaseUrl,
|
|
562
|
+
releaseAssetName: source.assetName,
|
|
563
|
+
releaseAssetUrl: source.assetUrl,
|
|
564
|
+
...extra
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
|
|
472
568
|
function normalizeVendoredReleaseVersion(releaseTag) {
|
|
473
569
|
const normalized = String(releaseTag || "").trim().replace(/^v/u, "")
|
|
474
570
|
if (!normalized) {
|
|
@@ -556,6 +652,113 @@ async function downloadVendoredAsset(url, destinationPath) {
|
|
|
556
652
|
}
|
|
557
653
|
}
|
|
558
654
|
|
|
655
|
+
async function downloadAndExtractVendoredPackage(source, stagingRoot, extractRoot) {
|
|
656
|
+
const archivePath = path.join(stagingRoot, source.assetName)
|
|
657
|
+
await downloadVendoredAsset(source.assetUrl, archivePath)
|
|
658
|
+
return await extractVendoredArchive(
|
|
659
|
+
archivePath,
|
|
660
|
+
extractRoot,
|
|
661
|
+
source.archiveFormat === "zip" ? "zip" : "tar.gz"
|
|
662
|
+
)
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
async function resolveFallbackVendoredAssetSource(preferredSource, error) {
|
|
666
|
+
if (!isVendoredAssetMissingError(error)) {
|
|
667
|
+
return null
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
const releases = await listVendoredReleases(
|
|
671
|
+
preferredSource.baseUrl,
|
|
672
|
+
preferredSource.repository
|
|
673
|
+
)
|
|
674
|
+
|
|
675
|
+
for (const release of releases) {
|
|
676
|
+
const releaseTag = typeof release?.tag_name === "string" ? release.tag_name.trim() : ""
|
|
677
|
+
if (!releaseTag || releaseTag === preferredSource.releaseTag) {
|
|
678
|
+
continue
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
const candidate = createVendoredAssetSource({
|
|
682
|
+
repository: preferredSource.repository,
|
|
683
|
+
baseUrl: preferredSource.baseUrl,
|
|
684
|
+
releaseTag,
|
|
685
|
+
packageName: preferredSource.packageName,
|
|
686
|
+
platform: preferredSource.platform,
|
|
687
|
+
arch: preferredSource.arch,
|
|
688
|
+
archiveFormat: preferredSource.archiveFormat,
|
|
689
|
+
downloadCacheDir: preferredSource.downloadCacheDir
|
|
690
|
+
})
|
|
691
|
+
const assetNames = Array.isArray(release.assets)
|
|
692
|
+
? new Set(
|
|
693
|
+
release.assets
|
|
694
|
+
.map((asset) => (typeof asset?.name === "string" ? asset.name : ""))
|
|
695
|
+
.filter(Boolean)
|
|
696
|
+
)
|
|
697
|
+
: new Set()
|
|
698
|
+
|
|
699
|
+
if (assetNames.has(candidate.assetName)) {
|
|
700
|
+
return candidate
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
return null
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
async function listVendoredReleases(baseUrl, repository) {
|
|
708
|
+
const response = await globalThis.fetch(buildVendoredReleasesApiUrl(baseUrl, repository), {
|
|
709
|
+
headers: buildVendoredReleaseApiHeaders()
|
|
710
|
+
})
|
|
711
|
+
|
|
712
|
+
if (!response.ok) {
|
|
713
|
+
throw new Error(
|
|
714
|
+
`Failed to query vendored releases for ${repository}: HTTP ${response.status}`
|
|
715
|
+
)
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
const releases = await response.json()
|
|
719
|
+
return Array.isArray(releases) ? releases : []
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
function buildVendoredReleasesApiUrl(baseUrl, repository) {
|
|
723
|
+
const normalizedBaseUrl = String(baseUrl || "https://github.com").replace(/\/+$/u, "")
|
|
724
|
+
|
|
725
|
+
try {
|
|
726
|
+
const parsedUrl = new URL(normalizedBaseUrl)
|
|
727
|
+
|
|
728
|
+
if (parsedUrl.hostname === "github.com") {
|
|
729
|
+
return `https://api.github.com/repos/${repository}/releases?per_page=20`
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
if (parsedUrl.hostname === "api.github.com") {
|
|
733
|
+
return `${normalizedBaseUrl}/repos/${repository}/releases?per_page=20`
|
|
734
|
+
}
|
|
735
|
+
} catch {
|
|
736
|
+
// Fall through to the generic repository API path.
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
return `${normalizedBaseUrl}/repos/${repository}/releases?per_page=20`
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
function buildVendoredReleaseApiHeaders() {
|
|
743
|
+
const token =
|
|
744
|
+
process.env.GITHUB_TOKEN?.trim() ||
|
|
745
|
+
process.env.GH_TOKEN?.trim() ||
|
|
746
|
+
process.env.HAGISCRIPT_GITHUB_TOKEN?.trim()
|
|
747
|
+
|
|
748
|
+
return {
|
|
749
|
+
Accept: "application/vnd.github+json",
|
|
750
|
+
"User-Agent": "hagiscript-vendored-runtime",
|
|
751
|
+
...(token ? { Authorization: `Bearer ${token}` } : {})
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
function isVendoredAssetMissingError(error) {
|
|
756
|
+
return Boolean(
|
|
757
|
+
error instanceof Error &&
|
|
758
|
+
/Failed to download vendored asset .*: HTTP 404$/u.test(error.message.trim())
|
|
759
|
+
)
|
|
760
|
+
}
|
|
761
|
+
|
|
559
762
|
async function extractVendoredArchive(archivePath, stagingDirectory, archiveKind) {
|
|
560
763
|
await rm(stagingDirectory, { recursive: true, force: true })
|
|
561
764
|
await mkdir(stagingDirectory, { recursive: true })
|