@scriptc/compiler 0.0.33 → 0.0.35

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 (133) hide show
  1. package/dist/backend/cc-cache.test.js +1091 -7
  2. package/dist/backend/cc-cache.test.js.map +1 -1
  3. package/dist/backend/cc.d.ts +109 -18
  4. package/dist/backend/cc.js +1057 -289
  5. package/dist/backend/cc.js.map +1 -1
  6. package/dist/backend/emission/emit-exprs.js +26 -1
  7. package/dist/backend/emission/emit-exprs.js.map +1 -1
  8. package/dist/backend/emission/emit-shapes.js +37 -4
  9. package/dist/backend/emission/emit-shapes.js.map +1 -1
  10. package/dist/backend/emission/emitter.d.ts +11 -2
  11. package/dist/backend/emission/emitter.js +11 -5
  12. package/dist/backend/emission/emitter.js.map +1 -1
  13. package/dist/backend/library-identity.d.ts +22 -0
  14. package/dist/backend/library-identity.js +103 -0
  15. package/dist/backend/library-identity.js.map +1 -0
  16. package/dist/backend/llvm/emitter.d.ts +3 -0
  17. package/dist/backend/llvm/emitter.js +35 -5
  18. package/dist/backend/llvm/emitter.js.map +1 -1
  19. package/dist/backend/llvm/shapes.d.ts +1 -0
  20. package/dist/backend/llvm/shapes.js +25 -1
  21. package/dist/backend/llvm/shapes.js.map +1 -1
  22. package/dist/backend/llvm/split.d.ts +43 -0
  23. package/dist/backend/llvm/split.js +227 -0
  24. package/dist/backend/llvm/split.js.map +1 -0
  25. package/dist/backend/llvm/split.test.d.ts +1 -0
  26. package/dist/backend/llvm/split.test.js +102 -0
  27. package/dist/backend/llvm/split.test.js.map +1 -0
  28. package/dist/backend/mangle.d.ts +1 -0
  29. package/dist/backend/mangle.js +3 -0
  30. package/dist/backend/mangle.js.map +1 -1
  31. package/dist/backend/object-localize.d.ts +5 -5
  32. package/dist/backend/object-localize.js +19 -16
  33. package/dist/backend/object-localize.js.map +1 -1
  34. package/dist/backend/object-localize.test.js +57 -13
  35. package/dist/backend/object-localize.test.js.map +1 -1
  36. package/dist/executable/early-cache.d.ts +86 -0
  37. package/dist/executable/early-cache.js +460 -0
  38. package/dist/executable/early-cache.js.map +1 -0
  39. package/dist/executable/early-cache.test.d.ts +1 -0
  40. package/dist/executable/early-cache.test.js +300 -0
  41. package/dist/executable/early-cache.test.js.map +1 -0
  42. package/dist/ffi/profile.d.ts +7 -2
  43. package/dist/ffi/profile.js +1 -0
  44. package/dist/ffi/profile.js.map +1 -1
  45. package/dist/frontend/input-tracker.d.ts +82 -0
  46. package/dist/frontend/input-tracker.js +380 -0
  47. package/dist/frontend/input-tracker.js.map +1 -0
  48. package/dist/frontend/input-tracker.test.d.ts +1 -0
  49. package/dist/frontend/input-tracker.test.js +137 -0
  50. package/dist/frontend/input-tracker.test.js.map +1 -0
  51. package/dist/frontend/lowering/lower-builtins.js +4 -10
  52. package/dist/frontend/lowering/lower-builtins.js.map +1 -1
  53. package/dist/frontend/lowering/lower-calls.js +169 -144
  54. package/dist/frontend/lowering/lower-calls.js.map +1 -1
  55. package/dist/frontend/lowering/lower-classes.js +3 -0
  56. package/dist/frontend/lowering/lower-classes.js.map +1 -1
  57. package/dist/frontend/lowering/lower-containers.js +66 -32
  58. package/dist/frontend/lowering/lower-containers.js.map +1 -1
  59. package/dist/frontend/lowering/lower-exprs.js +89 -11
  60. package/dist/frontend/lowering/lower-exprs.js.map +1 -1
  61. package/dist/frontend/lowering/lower-inspect.js +45 -26
  62. package/dist/frontend/lowering/lower-inspect.js.map +1 -1
  63. package/dist/frontend/lowering/lower-island.js +3 -3
  64. package/dist/frontend/lowering/lower-island.js.map +1 -1
  65. package/dist/frontend/lowering/lower-mixins.js +1 -1
  66. package/dist/frontend/lowering/lower-mixins.js.map +1 -1
  67. package/dist/frontend/lowering/lower-modules.js +1 -1
  68. package/dist/frontend/lowering/lower-modules.js.map +1 -1
  69. package/dist/frontend/lowering/lower-stmts.js +49 -4
  70. package/dist/frontend/lowering/lower-stmts.js.map +1 -1
  71. package/dist/frontend/lowering/lowerer.d.ts +73 -29
  72. package/dist/frontend/lowering/lowerer.js +588 -116
  73. package/dist/frontend/lowering/lowerer.js.map +1 -1
  74. package/dist/frontend/lowering/surfaces.js +1 -1
  75. package/dist/frontend/lowering/surfaces.js.map +1 -1
  76. package/dist/frontend/npm-static-rewrite.js +25 -26
  77. package/dist/frontend/npm-static-rewrite.js.map +1 -1
  78. package/dist/frontend/npm-static.js +29 -30
  79. package/dist/frontend/npm-static.js.map +1 -1
  80. package/dist/frontend/npm-static.test.d.ts +1 -0
  81. package/dist/frontend/npm-static.test.js +15 -0
  82. package/dist/frontend/npm-static.test.js.map +1 -0
  83. package/dist/frontend/npm.js +5 -17
  84. package/dist/frontend/npm.js.map +1 -1
  85. package/dist/frontend/program.js +44 -19
  86. package/dist/frontend/program.js.map +1 -1
  87. package/dist/frontend/resolve.js +8 -29
  88. package/dist/frontend/resolve.js.map +1 -1
  89. package/dist/frontend/ts7/checker.d.ts +40 -2
  90. package/dist/frontend/ts7/checker.js +194 -32
  91. package/dist/frontend/ts7/checker.js.map +1 -1
  92. package/dist/frontend/ts7/program.js +17 -18
  93. package/dist/frontend/ts7/program.js.map +1 -1
  94. package/dist/frontend/types.d.ts +33 -0
  95. package/dist/frontend/types.js +134 -45
  96. package/dist/frontend/types.js.map +1 -1
  97. package/dist/index.d.ts +5 -2
  98. package/dist/index.js +458 -141
  99. package/dist/index.js.map +1 -1
  100. package/dist/ir/nodes.d.ts +27 -4
  101. package/dist/ir/nodes.js +28 -0
  102. package/dist/ir/nodes.js.map +1 -1
  103. package/dist/ir/serialize.d.ts +1 -1
  104. package/dist/ir/serialize.js +1 -1
  105. package/dist/ir/validate.js +37 -1
  106. package/dist/ir/validate.js.map +1 -1
  107. package/dist/library/early-cache.d.ts +65 -0
  108. package/dist/library/early-cache.js +361 -0
  109. package/dist/library/early-cache.js.map +1 -0
  110. package/dist/library/early-cache.test.d.ts +1 -0
  111. package/dist/library/early-cache.test.js +528 -0
  112. package/dist/library/early-cache.test.js.map +1 -0
  113. package/dist/library/implementation-identity.d.ts +23 -0
  114. package/dist/library/implementation-identity.js +84 -0
  115. package/dist/library/implementation-identity.js.map +1 -0
  116. package/dist/library/int-infer.js +14 -0
  117. package/dist/library/int-infer.js.map +1 -1
  118. package/dist/library/int-infer.test.js +21 -4
  119. package/dist/library/int-infer.test.js.map +1 -1
  120. package/dist/library/semantic-source.d.ts +13 -0
  121. package/dist/library/semantic-source.js +257 -0
  122. package/dist/library/semantic-source.js.map +1 -0
  123. package/dist/library/semantic-source.test.d.ts +1 -0
  124. package/dist/library/semantic-source.test.js +113 -0
  125. package/dist/library/semantic-source.test.js.map +1 -0
  126. package/dist/library/sidecar.d.ts +6 -0
  127. package/dist/library/sidecar.js +11 -0
  128. package/dist/library/sidecar.js.map +1 -1
  129. package/dist/startup-cache.d.ts +6 -0
  130. package/dist/startup-cache.js +6 -0
  131. package/dist/startup-cache.js.map +1 -0
  132. package/package.json +3 -2
  133. package/surface-manifest.json +1 -1
package/dist/index.js CHANGED
@@ -1,14 +1,16 @@
1
- import { mkdir, rm, writeFile } from "node:fs/promises";
1
+ import { mkdir, readFile, rm, writeFile } from "node:fs/promises";
2
2
  import { basename, dirname, join, resolve } from "node:path";
3
- import { CcCompileError, compileC, compileLibArchive, mobileLibraryTarget, mobileTargetRefusal, resolveCc, targetPlatform } from "./backend/cc.js";
3
+ import { buildCacheRoot, CcCompileError, compileC, compileLibArchive, executableNativeEnvironmentFingerprint, mobileLibraryTarget, mobileTargetRefusal, prepareBuildCacheRoot, pruneBuildCache, resolveCc, targetPlatform } from "./backend/cc.js";
4
4
  import { emitModule } from "./backend/emission/emitter.js";
5
5
  import { emitLlvmModule, LlvmUnsupportedError } from "./backend/llvm/emitter.js";
6
+ import { splitLlvmLibraryProgram, splitLlvmProgram } from "./backend/llvm/split.js";
7
+ import { rebaseLibrarySourceComments, replaceLibraryIdentity, stripLibraryIdentity, stripLibrarySourceComments } from "./backend/library-identity.js";
6
8
  import { checkerPanicDiag, ffiNativeBuildDiag, libAsyncExportDiag, libAsyncSurfaceDiag, libExportUnresolvedDiag, libGenericExportDiag, libIntBoundaryDiag, libNpmIneligibleDiag, libSidecarDiag, libUnmappableSignatureDiag, iceDiag, isCheckerPanic, LIB_INBOUND_BYTES_TRAP_CODE, LIB_RUNTIME_TRAP_CODES } from "./diagnostics/diagnostic.js";
7
9
  import { checkLibraryIntegerSlots, classSeed, hasIntSlots, numberCarrierKind } from "./library/int-infer.js";
8
10
  import { loadLibraryProfile, profileRemediation, profileTeaching } from "./library/profile.js";
9
11
  import { decorateLibraryRefusals, evaluateLibraryFences } from "./library/fence-eval.js";
10
12
  import { assembleTrapTeaching } from "./library/trap-teaching.js";
11
- import { buildSidecar, canonicalModuleGraph, canonicalPath, compilerReleaseVersion, libraryIdentityHashes, } from "./library/sidecar.js";
13
+ import { buildSidecar, canonicalModuleGraph, canonicalPath, compilerReleaseVersion, libraryIdentityHashes, updateSidecarIdentity, } from "./library/sidecar.js";
12
14
  import { validateSidecar } from "./library/sidecar-validate.js";
13
15
  import { entryFunctionExports } from "./frontend/lib-exports.js";
14
16
  import { entryContractFacts } from "./frontend/lib-contract.js";
@@ -18,13 +20,18 @@ import { validateModule } from "./ir/validate.js";
18
20
  import { canonicalBuiltinModule, checkPreflight, isNodeTypesPath, loadProgram, locOf, requiresOf, resolveNpmImport } from "./frontend/program.js";
19
21
  import { npmStaticIneligibleReason, npmStaticOffenders, npmStaticPackageOfPath } from "./frontend/npm-static.js";
20
22
  import { provenanceSources } from "./frontend/provenance-registry.js";
21
- import { resolveBareModule } from "./frontend/resolve.js";
23
+ import { clearResolveCaches, resolveBareModule } from "./frontend/resolve.js";
22
24
  import { isJsSourceFileName, isRelativeSpecifier } from "./frontend/shared.js";
23
25
  import { lowerToIr } from "./frontend/lowering/lowerer.js";
24
26
  import { loadFfiProfile } from "./ffi/profile.js";
25
27
  import { hasForeignFfiCallback } from "./backend/ffi-callbacks.js";
28
+ import { FrontendInputTracker, trackedReadFile } from "./frontend/input-tracker.js";
29
+ import { libraryFrontendImplementationFingerprint, publishEarlyLibraryCache, readEarlyLibraryCache, readSemanticLibraryCache } from "./library/early-cache.js";
30
+ import { createSourceLineRebaser } from "./library/semantic-source.js";
31
+ import { publishEarlyExecutableCache, publishEarlyExecutableRoute, readEarlyExecutableCache } from "./executable/early-cache.js";
32
+ import { compilerImplementationIdentity } from "./library/implementation-identity.js";
26
33
  export const VERSION = "0.0.1";
27
- export { compileC, runtimeSrcDir } from "./backend/cc.js";
34
+ export { compileC, runtimeSrcDir, warmNativeCaches, } from "./backend/cc.js";
28
35
  export { ANDROID_MIN_API, IPHONEOS_MIN_VERSION, isAndroidTarget, isIosTarget, isMobileTarget, mobileLibraryTarget, mobileTargetRefusal } from "./backend/cc.js";
29
36
  export { emitModule } from "./backend/emission/emitter.js";
30
37
  export { renderAll, renderDiagnostic } from "./diagnostics/render.js";
@@ -317,6 +324,11 @@ function packageNameOfBareSpecifier(specifier) {
317
324
  * static-or-refuse teaching, and the detection closes over the opted-in
318
325
  * packages' own bare edges (no island exists to serve a dep from). */
319
326
  function runFrontend(entryPath, npmStatic, externalTypes) {
327
+ // Resolver package/workspace metadata is intentionally shared across the
328
+ // several load attempts of ONE auto-detection fixpoint, but never across
329
+ // separate compiles in a long-lived process. A cache miss must observe
330
+ // package.json edits before it can publish a new early-library entry.
331
+ clearResolveCaches();
320
332
  const statuses = [];
321
333
  const npmSites = new Map();
322
334
  const judged = new Set();
@@ -620,13 +632,112 @@ export function analyze(entryPath, opts = {}) {
620
632
  }
621
633
  /** The whole pipeline: load → preflight → lower → validate → emit C → clang. */
622
634
  export async function compile(entryPath, opts) {
635
+ const frontendInputs = new FrontendInputTracker();
636
+ return frontendInputs.run(() => compileTracked(entryPath, opts, frontendInputs));
637
+ }
638
+ function executableNativeFeatures(mod, backend, dynamic, optimization, llvmRefusal) {
639
+ return {
640
+ backend,
641
+ ...(optimization === "dev" ? { optimization: "dev" } : {}),
642
+ ...(llvmRefusal === undefined ? {} : { llvmRefusal }),
643
+ dynamic,
644
+ regex: moduleUsesRegex(mod),
645
+ copying: moduleUsesCopying(mod),
646
+ textDecoderLegacy: moduleUsesLegacyTextDecoder(mod),
647
+ fileHandle: moduleUsesFileHandle(mod),
648
+ fetch: moduleUsesFetch(mod),
649
+ netIsland: moduleEmbedsBuiltin(mod, "node:http") ||
650
+ moduleEmbedsBuiltin(mod, "node:https") ||
651
+ moduleEmbedsBuiltin(mod, "node:net") ||
652
+ moduleEmbedsBuiltin(mod, "node:tls"),
653
+ zlib: moduleUsesZlib(mod) || moduleEmbedsCompressedNpm(mod),
654
+ assert: moduleUsesAssert(mod),
655
+ inspect: moduleUsesInspect(mod),
656
+ dynInvoke: moduleUsesDynInvoke(mod),
657
+ dc: moduleUsesDc(mod),
658
+ dynAsync: moduleUsesDynAsync(mod),
659
+ events: moduleUsesProcessEvents(mod),
660
+ emitter: moduleUsesEmitter(mod),
661
+ symbol: moduleUsesSymbol(mod),
662
+ searchParams: moduleUsesSearchParams(mod),
663
+ qs: moduleUsesQs(mod),
664
+ parseArgs: moduleUsesParseArgs(mod),
665
+ stream: moduleUsesStream(mod),
666
+ net: moduleUsesNet(mod),
667
+ http: moduleUsesHttpServer(mod),
668
+ http2: moduleUsesHttp2(mod),
669
+ dgram: moduleUsesDgram(mod),
670
+ watch: moduleUsesFsWatch(mod),
671
+ foreignFfi: hasForeignFfiCallback(mod.ffiImports ?? []),
672
+ nodeTest: moduleUsesNodeTest(mod),
673
+ tls: moduleUsesTls(mod),
674
+ tlsCa: moduleUsesTlsCa(mod),
675
+ };
676
+ }
677
+ async function compileExecutableNative(features, cPath, outPath, sanitize, ffi, programSplit = null, onArtifactReady) {
678
+ const effectiveProgramSplit = programSplit ??
679
+ (features.optimization === "dev" && features.backend === "llvm" && !sanitize
680
+ ? splitLlvmProgram(await readFile(cPath, "utf8"))
681
+ : null);
682
+ await compileC({
683
+ cPath,
684
+ outPath,
685
+ cacheIdentity: "scriptc-generated-v1",
686
+ ...(features.optimization === "dev" ? { optimization: "dev" } : {}),
687
+ ...(effectiveProgramSplit === null
688
+ ? {}
689
+ : {
690
+ programShards: effectiveProgramSplit.shards,
691
+ programPublicSymbols: effectiveProgramSplit.publicSymbols,
692
+ }),
693
+ sanitize,
694
+ dynamic: features.dynamic,
695
+ regex: features.regex,
696
+ copying: features.copying,
697
+ textDecoderLegacy: features.textDecoderLegacy,
698
+ fileHandle: features.fileHandle,
699
+ fetch: features.fetch,
700
+ netIsland: features.netIsland,
701
+ zlib: features.zlib,
702
+ assert: features.assert,
703
+ inspect: features.inspect,
704
+ dynInvoke: features.dynInvoke,
705
+ dc: features.dc,
706
+ dynAsync: features.dynAsync,
707
+ events: features.events,
708
+ emitter: features.emitter,
709
+ symbol: features.symbol,
710
+ searchParams: features.searchParams,
711
+ qs: features.qs,
712
+ parseArgs: features.parseArgs,
713
+ stream: features.stream,
714
+ net: features.net,
715
+ http: features.http,
716
+ http2: features.http2,
717
+ dgram: features.dgram,
718
+ watch: features.watch,
719
+ foreignFfi: features.foreignFfi,
720
+ nodeTest: features.nodeTest,
721
+ tls: features.tls,
722
+ tlsCa: features.tlsCa,
723
+ ...(onArtifactReady === undefined ? {} : { onArtifactReady }),
724
+ ...(ffi === null
725
+ ? {}
726
+ : { linkInputs: ffi.libraries, systemLibraries: ffi.systemLibraries }),
727
+ });
728
+ }
729
+ async function compileTracked(entryPath, opts, frontendInputs) {
730
+ entryPath = resolve(entryPath);
623
731
  let ffi = null;
732
+ let ffiProfileBytes = null;
624
733
  if (opts.ffiProfilePath !== undefined) {
625
- const loaded = loadFfiProfile(opts.ffiProfilePath);
734
+ const ffiProfilePath = resolve(opts.ffiProfilePath);
735
+ const loaded = loadFfiProfile(ffiProfilePath);
626
736
  if (!loaded.ok) {
627
737
  return { ok: false, diagnostics: loaded.diagnostics, sourceTexts: new Map() };
628
738
  }
629
739
  ffi = loaded.profile;
740
+ ffiProfileBytes = loaded.profileBytes;
630
741
  }
631
742
  const buildPlatform = buildTargetPlatform();
632
743
  // Mobile triples are library-mode targets: the archive an embedding app
@@ -655,6 +766,81 @@ export async function compile(entryPath, opts) {
655
766
  };
656
767
  }
657
768
  }
769
+ const cacheRoot = provenanceSources() === null
770
+ ? await prepareBuildCacheRoot(buildCacheRoot())
771
+ : null;
772
+ const implementation = await compilerImplementationIdentity();
773
+ const earlyCacheOptions = {
774
+ entryPath,
775
+ outDir: opts.outDir,
776
+ outPath: opts.outPath,
777
+ emitIr: opts.emitIr ?? false,
778
+ sanitize: opts.sanitize ?? false,
779
+ dynamic: opts.dynamic ?? false,
780
+ backend: opts.backend ?? "auto",
781
+ ...(opts.optimization === "dev" ? { optimization: "dev" } : {}),
782
+ npmStatic: opts.npmStatic ?? null,
783
+ ffiProfile: opts.ffiProfilePath === undefined || ffiProfileBytes === null
784
+ ? null
785
+ : { path: opts.ffiProfilePath, bytes: ffiProfileBytes },
786
+ target: `${process.env["SCRIPTC_TARGET"] ?? "native"}:${buildPlatform}:${process.arch}`,
787
+ compiler: [process.env["SCRIPTC_CC"] ?? "clang"],
788
+ nativeEnvironment: await executableNativeEnvironmentFingerprint(),
789
+ nodeVersion: process.version,
790
+ implementation: implementation.digest,
791
+ implementationDependencies: implementation.dependencies,
792
+ };
793
+ const earlyHit = await readEarlyExecutableCache(cacheRoot, earlyCacheOptions);
794
+ if (earlyHit !== null) {
795
+ // Route/proof metadata is independently evictable. A full-compiler
796
+ // fallback that still finds the validated payload repairs that lightweight
797
+ // index so the next identical CLI invocation can avoid this module graph.
798
+ await publishEarlyExecutableRoute(cacheRoot, earlyCacheOptions).catch(() => undefined);
799
+ if (earlyHit.executableRestored) {
800
+ await pruneBuildCache(cacheRoot);
801
+ return {
802
+ ok: true,
803
+ binaryPath: opts.outPath,
804
+ cPath: earlyHit.cPath,
805
+ backend: earlyHit.native.backend,
806
+ ...(earlyHit.irPath === undefined ? {} : { irPath: earlyHit.irPath }),
807
+ ...(earlyHit.native.llvmRefusal === undefined
808
+ ? {}
809
+ : { llvmRefusal: earlyHit.native.llvmRefusal }),
810
+ };
811
+ }
812
+ try {
813
+ await compileExecutableNative(earlyHit.native, earlyHit.cPath, opts.outPath, opts.sanitize ?? false, ffi, null, async ({ dependencies }) => {
814
+ await publishEarlyExecutableCache(cacheRoot, earlyCacheOptions, {
815
+ ...earlyHit,
816
+ executableRestored: true,
817
+ nativeDependencies: dependencies,
818
+ frontend: earlyHit.frontend,
819
+ });
820
+ });
821
+ }
822
+ catch (err) {
823
+ if (ffi !== null && err instanceof CcCompileError) {
824
+ return {
825
+ ok: false,
826
+ diagnostics: [ffiNativeBuildDiag(ffiNativeBuildDetail(err), opts.ffiProfilePath ?? entryPath)],
827
+ sourceTexts: new Map(),
828
+ };
829
+ }
830
+ throw err;
831
+ }
832
+ await pruneBuildCache(cacheRoot);
833
+ return {
834
+ ok: true,
835
+ binaryPath: opts.outPath,
836
+ cPath: earlyHit.cPath,
837
+ backend: earlyHit.native.backend,
838
+ ...(earlyHit.irPath === undefined ? {} : { irPath: earlyHit.irPath }),
839
+ ...(earlyHit.native.llvmRefusal === undefined
840
+ ? {}
841
+ : { llvmRefusal: earlyHit.native.llvmRefusal }),
842
+ };
843
+ }
658
844
  const fe = runFrontend(entryPath, opts.npmStatic);
659
845
  let lowered;
660
846
  let entryText;
@@ -728,6 +914,7 @@ export async function compile(entryPath, opts) {
728
914
  // (the frontend ran once, the IR is backend-agnostic — nothing recompiles).
729
915
  let cPath = join(opts.outDir, `${stem}.c`);
730
916
  let backend = "c";
917
+ let llvmSource = null;
731
918
  let llvmRefusal;
732
919
  if (opts.backend !== "c") {
733
920
  try {
@@ -737,6 +924,7 @@ export async function compile(entryPath, opts) {
737
924
  });
738
925
  cPath = join(opts.outDir, `${stem}.ll`);
739
926
  await writeFile(cPath, ll);
927
+ llvmSource = ll;
740
928
  backend = "llvm";
741
929
  }
742
930
  catch (err) {
@@ -763,105 +951,24 @@ export async function compile(entryPath, opts) {
763
951
  irPath = join(opts.outDir, `${stem}.ir.json`);
764
952
  await writeFile(irPath, serializeModule(lowered.module));
765
953
  }
954
+ const nativeFeatures = executableNativeFeatures(lowered.module, backend, opts.dynamic ?? false, opts.optimization ?? "release", llvmRefusal);
955
+ const programSplit = backend === "llvm" && (opts.optimization ?? "release") === "dev" &&
956
+ !(opts.sanitize ?? false) && llvmSource !== null
957
+ ? splitLlvmProgram(llvmSource)
958
+ : null;
766
959
  await mkdir(dirname(opts.outPath), { recursive: true });
960
+ let publishedExecutable = false;
767
961
  try {
768
- await compileC({
769
- cPath,
770
- outPath: opts.outPath,
771
- // The emitted TU's non-system dependencies are the runtime/vendor tree,
772
- // which cc.ts fingerprints separately. Low-level arbitrary C omits this
773
- // opt-in so same-path header edits can never hit a stale artifact.
774
- cacheIdentity: "scriptc-generated-v1",
775
- sanitize: opts.sanitize ?? false,
776
- dynamic: opts.dynamic ?? false,
777
- // The link switch for scr_regex.c + libregexp: detected on the IR, so
778
- // regex-free programs keep the historical (pinned) command line.
779
- regex: moduleUsesRegex(lowered.module),
780
- // Array copying methods and the typed-array bridges live in one
781
- // optional TU, linked only when one of their IR intrinsics survives.
782
- copying: moduleUsesCopying(lowered.module),
783
- textDecoderLegacy: moduleUsesLegacyTextDecoder(lowered.module),
784
- // FileHandle owns a native descriptor and its settled-promise adapters;
785
- // keep that optional TU out of programs that never call fsp.open.
786
- fileHandle: moduleUsesFileHandle(lowered.module),
787
- // The link switch for scr_fetch.c (the native bridge over scr_net +
788
- // scr_tls + scr_http's client parser + zlib — cc.ts implies those
789
- // units into the link): embedded npm code that references fetch gets
790
- // the bridge; everything else keeps its exact link line.
791
- fetch: moduleUsesFetch(lowered.module),
792
- // The island's node:http/https client bridge: embedded graphs that
793
- // import those builtins pull scr_net_island.c + the socket units.
794
- netIsland: moduleEmbedsBuiltin(lowered.module, "node:http") ||
795
- moduleEmbedsBuiltin(lowered.module, "node:https") ||
796
- moduleEmbedsBuiltin(lowered.module, "node:net") ||
797
- moduleEmbedsBuiltin(lowered.module, "node:tls"),
798
- // The link switch for scr_zlib.c + libz: zlib.* libCalls on the IR,
799
- // node:zlib in the embedded graph, or COMPRESSED embedded module text
800
- // (emit-island.ts stores big npm sources as raw DEFLATE; the emitted
801
- // main installs scr_zlib_inflate_exact on the same predicate).
802
- zlib: moduleUsesZlib(lowered.module) || moduleEmbedsCompressedNpm(lowered.module),
803
- // The link switch for scr_assert.c: assert.* libCalls on the IR (the
804
- // regex switch also pulls it — scr_regex.c calls the assert helpers).
805
- assert: moduleUsesAssert(lowered.module),
806
- // The link switch for scr_inspect.c: insp.* libCalls on the IR.
807
- inspect: moduleUsesInspect(lowered.module),
808
- // The link switch for scr_dyn_invoke.c: dynInvoke nodes or
809
- // dyn.defineProps libCalls on the IR.
810
- dynInvoke: moduleUsesDynInvoke(lowered.module),
811
- // The link switch for scr_dc.c: dc.* libCalls on the IR (the
812
- // diagnostics_channel registry and pub/sub).
813
- dc: moduleUsesDc(lowered.module),
814
- // The link switch for scr_async_dyn.c: the checked-dynamic async
815
- // surfaces (cc.ts also pulls it under the dynInvoke/dc gates).
816
- dynAsync: moduleUsesDynAsync(lowered.module),
817
- // The link switch for scr_events.c: process signal/exit listeners and
818
- // the stdin event surface on the IR.
819
- events: moduleUsesProcessEvents(lowered.module),
820
- // The link switch for scr_events_emitter.c: the node:events
821
- // EventEmitter surface on the IR (emitter.* libCalls or the
822
- // %EventEmitter class def).
823
- emitter: moduleUsesEmitter(lowered.module),
824
- // The link switch for scr_symbol.c: sym.* libCalls or a symbol-kind
825
- // type anywhere on the IR.
826
- symbol: moduleUsesSymbol(lowered.module),
827
- // The link switch for scr_url_params.c: sp.* libCalls, the
828
- // url.searchParams getter, or a searchParams-kind type on the IR.
829
- searchParams: moduleUsesSearchParams(lowered.module),
830
- // The link switch for scr_qs.c: the qs.* libCalls that live there
831
- // (parse/stringify/unescape; escape rides the always-linked encoder).
832
- qs: moduleUsesQs(lowered.module),
833
- // The static node:util parseArgs implementation (scr_util.c).
834
- parseArgs: moduleUsesParseArgs(lowered.module),
835
- // The link switch for scr_stream.c: the node:stream class surface on
836
- // the IR (stream libCalls or the %Readable-family class defs).
837
- stream: moduleUsesStream(lowered.module),
838
- // The link switch for scr_net.c: net.* (or http.* — http rides on
839
- // net) libCalls on the IR.
840
- net: moduleUsesNet(lowered.module),
841
- // The link switch for scr_http.c: http.* libCalls on the IR.
842
- http: moduleUsesHttpServer(lowered.module),
843
- http2: moduleUsesHttp2(lowered.module),
844
- // The link switch for scr_dgram.c: dgram.* or dns.* libCalls on the IR.
845
- dgram: moduleUsesDgram(lowered.module),
846
- // The link switch for scr_watch.c: fs.watch/watcher.* libCalls on the IR.
847
- watch: moduleUsesFsWatch(lowered.module),
848
- foreignFfi: hasForeignFfiCallback(lowered.module.ffiImports ?? []),
849
- // The link switch for scr_test.c: test.* libCalls on the IR.
850
- nodeTest: moduleUsesNodeTest(lowered.module),
851
- // The link switch for scr_tls.c + the vendored mbedTLS archive:
852
- // tls.* or https.* libCalls on the IR.
853
- tls: moduleUsesTls(lowered.module),
854
- // The link switch for scr_tls_ca.c (the CA-store introspection unit
855
- // — plain PEM bookkeeping, no mbedTLS): tlsca.* libCalls on the IR.
856
- // cc.ts also compiles it under the tls gate (scr_tls.c consults the
857
- // unit's default-set override for its trust anchors).
858
- tlsCa: moduleUsesTlsCa(lowered.module),
859
- ...(ffi !== null
860
- ? {
861
- linkInputs: ffi.libraries,
862
- systemLibraries: ffi.systemLibraries,
863
- }
864
- : {}),
962
+ await compileExecutableNative(nativeFeatures, cPath, opts.outPath, opts.sanitize ?? false, ffi, programSplit, async ({ dependencies }) => {
963
+ await publishEarlyExecutableCache(cacheRoot, earlyCacheOptions, {
964
+ cPath,
965
+ native: nativeFeatures,
966
+ executableRestored: true,
967
+ nativeDependencies: dependencies,
968
+ frontend: frontendInputs.snapshot(),
969
+ ...(irPath === undefined ? {} : { irPath }),
970
+ });
971
+ publishedExecutable = true;
865
972
  });
866
973
  }
867
974
  catch (err) {
@@ -876,6 +983,16 @@ export async function compile(entryPath, opts) {
876
983
  }
877
984
  throw err;
878
985
  }
986
+ if (!publishedExecutable) {
987
+ await publishEarlyExecutableCache(cacheRoot, earlyCacheOptions, {
988
+ cPath,
989
+ native: nativeFeatures,
990
+ executableRestored: false,
991
+ frontend: frontendInputs.snapshot(),
992
+ ...(irPath === undefined ? {} : { irPath }),
993
+ }).catch(() => undefined);
994
+ }
995
+ await pruneBuildCache(cacheRoot);
879
996
  return {
880
997
  ok: true,
881
998
  binaryPath: opts.outPath,
@@ -1202,7 +1319,176 @@ function mergeSidecarIntSlots(cfg, facts, mod) {
1202
1319
  }
1203
1320
  return { ok: true, config: cfg };
1204
1321
  }
1322
+ function libraryNativeFeatures(mod, backend) {
1323
+ return {
1324
+ backend,
1325
+ regex: moduleUsesRegex(mod),
1326
+ assert: moduleUsesAssert(mod),
1327
+ inspect: moduleUsesInspect(mod),
1328
+ symbol: moduleUsesSymbol(mod),
1329
+ searchParams: moduleUsesSearchParams(mod),
1330
+ emitter: moduleUsesEmitter(mod),
1331
+ zlib: moduleUsesZlib(mod),
1332
+ copying: moduleUsesCopying(mod),
1333
+ textDecoderLegacy: moduleUsesLegacyTextDecoder(mod),
1334
+ ...(mod.lib?.identity !== undefined ? { buildId: mod.lib.identity.buildId } : {}),
1335
+ };
1336
+ }
1337
+ function libraryLocalizeSymbols(profile) {
1338
+ return profile.localizeRuntime
1339
+ ? [
1340
+ profile.initSymbol,
1341
+ profile.sinkRegisterSymbol,
1342
+ ...(profile.collectSymbol !== null ? [profile.collectSymbol] : []),
1343
+ ...(profile.resultResetSymbol !== null ? [profile.resultResetSymbol] : []),
1344
+ ...(profile.callbackRegisterSymbol !== null ? [profile.callbackRegisterSymbol] : []),
1345
+ ...(profile.sidecar !== null
1346
+ ? [profile.sidecar.buildIdSymbol, profile.sidecar.abiVersionSymbol]
1347
+ : []),
1348
+ ...profile.exports.map((entry) => entry.symbol),
1349
+ ]
1350
+ : undefined;
1351
+ }
1352
+ async function compileLibraryNative(profile, cPath, archivePath, sanitize, features) {
1353
+ const localizeSymbols = libraryLocalizeSymbols(profile);
1354
+ let identityCSource;
1355
+ let programSource;
1356
+ if (profile.sidecar !== null || profile.emission === "c" ||
1357
+ (profile.emission === "llvm" && profile.optimization === "dev" && !sanitize)) {
1358
+ const publicSource = await readFile(cPath, "utf8");
1359
+ programSource = publicSource;
1360
+ }
1361
+ if (profile.sidecar !== null) {
1362
+ if (features.buildId === undefined)
1363
+ throw new Error("library identity TU has no build id");
1364
+ const withoutIdentity = stripLibraryIdentity(programSource, profile.emission);
1365
+ if (withoutIdentity === programSource) {
1366
+ throw new Error("generated public library TU has no identity region");
1367
+ }
1368
+ programSource = withoutIdentity;
1369
+ identityCSource = [
1370
+ "#include <stdint.h>",
1371
+ "#include <inttypes.h>",
1372
+ `uint64_t ${profile.sidecar.buildIdSymbol}(void) { return UINT64_C(0x${features.buildId}); }`,
1373
+ `uint32_t ${profile.sidecar.abiVersionSymbol}(void) { return ${profile.sidecar.abiVersion}u; }`,
1374
+ "",
1375
+ ].join("\n");
1376
+ }
1377
+ if (profile.emission === "c") {
1378
+ programSource = stripLibrarySourceComments(programSource, profile.entry);
1379
+ }
1380
+ const llvmSplit = profile.emission === "llvm" && profile.optimization === "dev" && !sanitize && programSource !== undefined
1381
+ ? splitLlvmLibraryProgram(programSource)
1382
+ : null;
1383
+ await compileLibArchive({
1384
+ cPath,
1385
+ ...(programSource !== undefined ? { programSource } : {}),
1386
+ ...(identityCSource !== undefined ? { identityCSource } : {}),
1387
+ ...(llvmSplit !== null
1388
+ ? {
1389
+ programShards: llvmSplit.shards,
1390
+ programPublicSymbols: llvmSplit.publicSymbols,
1391
+ }
1392
+ : {}),
1393
+ outPath: archivePath,
1394
+ cacheIdentity: "scriptc-generated-library-v1",
1395
+ sanitize,
1396
+ optimization: profile.optimization,
1397
+ ...(localizeSymbols !== undefined ? { localizeSymbols } : {}),
1398
+ ...(profile.instancePerThread ? { threadInstances: true } : {}),
1399
+ regex: features.regex,
1400
+ assert: features.assert,
1401
+ inspect: features.inspect,
1402
+ symbol: features.symbol,
1403
+ searchParams: features.searchParams,
1404
+ emitter: features.emitter,
1405
+ zlib: features.zlib,
1406
+ copying: features.copying,
1407
+ textDecoderLegacy: features.textDecoderLegacy,
1408
+ });
1409
+ }
1410
+ async function emitSemanticLibraryHit(hit, profile, opts, archivePath, cacheRoot, cacheOptions, timing) {
1411
+ const mod = hit.mod;
1412
+ const rootDir = dirname(resolve(opts.profilePath));
1413
+ let sidecarJson = hit.sidecarJson;
1414
+ if (profile.sidecar !== null) {
1415
+ if (mod.lib?.identity === undefined || sidecarJson === null) {
1416
+ throw new Error("semantic library cache lost sidecar identity metadata");
1417
+ }
1418
+ const modules = canonicalModuleGraph(rootDir, hit.sourceTexts);
1419
+ const { buildId, sourceHash } = libraryIdentityHashes(compilerReleaseVersion(), profile.profileBytes, modules);
1420
+ mod.lib.identity.buildId = buildId;
1421
+ hit.native.buildId = buildId;
1422
+ sidecarJson = updateSidecarIdentity(sidecarJson, buildId, sourceHash);
1423
+ }
1424
+ const validation = validateModule(mod);
1425
+ if (validation.length > 0) {
1426
+ return {
1427
+ ok: false,
1428
+ diagnostics: validation.map((violation) => iceDiag(violation.message, violation.loc)),
1429
+ sourceTexts: hit.sourceTexts,
1430
+ };
1431
+ }
1432
+ await mkdir(opts.outDir, { recursive: true });
1433
+ const stem = basename(profile.entry).replace(/\.(ts|js|mjs|cjs)$/, "");
1434
+ const cPath = join(opts.outDir, `${stem}.lib.${profile.emission === "llvm" ? "ll" : "c"}`);
1435
+ let translationUnit = hit.translationUnit;
1436
+ if (profile.sidecar !== null) {
1437
+ translationUnit = replaceLibraryIdentity(translationUnit, profile.emission, mod.lib.identity);
1438
+ }
1439
+ if (profile.emission === "llvm") {
1440
+ await writeFile(cPath, translationUnit);
1441
+ }
1442
+ else {
1443
+ const previous = hit.previousSources.get(mod.sourceFile);
1444
+ const current = hit.sourceTexts.get(mod.sourceFile);
1445
+ if (previous === undefined || current === undefined) {
1446
+ throw new Error("semantic library cache lost the entry source text");
1447
+ }
1448
+ translationUnit = rebaseLibrarySourceComments(translationUnit, mod.sourceFile, createSourceLineRebaser(mod.sourceFile, previous, current));
1449
+ await writeFile(cPath, translationUnit);
1450
+ }
1451
+ timing("semantic-tu-restore", { output_bytes: Buffer.byteLength(translationUnit) });
1452
+ await rm(join(opts.outDir, `${stem}.lib.${profile.emission === "llvm" ? "c" : "ll"}`), { force: true });
1453
+ let irPath;
1454
+ if (opts.emitIr) {
1455
+ irPath = join(opts.outDir, `${stem}.lib.ir.json`);
1456
+ await writeFile(irPath, serializeModule(mod));
1457
+ }
1458
+ await compileLibraryNative(profile, cPath, archivePath, opts.sanitize ?? false, hit.native);
1459
+ timing("native-archive");
1460
+ let sidecarPath;
1461
+ if (sidecarJson !== null) {
1462
+ sidecarPath = profile.sidecar.path !== null
1463
+ ? resolve(dirname(archivePath), profile.sidecar.path)
1464
+ : `${archivePath}.contract.json`;
1465
+ await writeFile(sidecarPath, sidecarJson);
1466
+ }
1467
+ await publishEarlyLibraryCache(cacheRoot, cacheOptions, {
1468
+ cPath,
1469
+ native: hit.native,
1470
+ frontend: hit.frontend,
1471
+ semantic: { mod, sources: hit.sourceTexts },
1472
+ ...(irPath !== undefined ? { irPath } : {}),
1473
+ ...(sidecarPath !== undefined ? { sidecarPath } : {}),
1474
+ }).catch(() => undefined);
1475
+ await pruneBuildCache(cacheRoot);
1476
+ timing("semantic-cache-publish");
1477
+ timing("complete");
1478
+ return {
1479
+ ok: true,
1480
+ archivePath,
1481
+ cPath,
1482
+ backend: profile.emission,
1483
+ ...(irPath !== undefined ? { irPath } : {}),
1484
+ ...(sidecarPath !== undefined ? { sidecarPath } : {}),
1485
+ };
1486
+ }
1205
1487
  export async function compileLibrary(opts) {
1488
+ const frontendInputs = new FrontendInputTracker();
1489
+ return frontendInputs.run(() => compileLibraryTracked(opts, frontendInputs));
1490
+ }
1491
+ async function compileLibraryTracked(opts, frontendInputs) {
1206
1492
  const timingOn = process.env["SCRIPTC_TIMING"] === "1";
1207
1493
  const timingStart = performance.now();
1208
1494
  let timingLast = timingStart;
@@ -1227,6 +1513,21 @@ export async function compileLibrary(opts) {
1227
1513
  const profile = loadedProfile.profile;
1228
1514
  const entryPath = profile.entry;
1229
1515
  const buildPlatform = buildTargetPlatform();
1516
+ const profileDir = dirname(resolve(opts.profilePath));
1517
+ for (let directory = dirname(entryPath);; directory = dirname(directory)) {
1518
+ for (const name of ["tsconfig.json", "package.json"]) {
1519
+ // Project configuration and package-realm metadata can affect the
1520
+ // frontend even when TypeScript did not request their bytes through
1521
+ // its delegated filesystem callbacks (tsgo may read them server-side).
1522
+ frontendInputs.run(() => {
1523
+ const path = join(directory, name);
1524
+ trackedReadFile(path);
1525
+ });
1526
+ }
1527
+ if (directory === profileDir || dirname(directory) === directory)
1528
+ break;
1529
+ }
1530
+ const archivePath = opts.outPath ?? join(opts.outDir, `${basename(entryPath).replace(/\.(ts|js|mjs|cjs)$/, "")}.lib.a`);
1230
1531
  // Mobile-target admission first — a pure env/host check, so a refused
1231
1532
  // pairing never reaches toolchain discovery. iOS targets (device and
1232
1533
  // simulator) build on darwin hosts only: the Apple SDK sysroot and the
@@ -1283,6 +1584,44 @@ export async function compileLibrary(opts) {
1283
1584
  };
1284
1585
  }
1285
1586
  }
1587
+ const cacheRoot = provenanceSources() === null
1588
+ ? await prepareBuildCacheRoot(buildCacheRoot())
1589
+ : null;
1590
+ const earlyCacheOptions = {
1591
+ profilePath: opts.profilePath,
1592
+ profileBytes: profile.profileBytes,
1593
+ entryPath,
1594
+ outDir: opts.outDir,
1595
+ ...(opts.outPath !== undefined ? { outPath: opts.outPath } : {}),
1596
+ emitIr: opts.emitIr ?? false,
1597
+ sanitize: opts.sanitize ?? false,
1598
+ target: `${process.env["SCRIPTC_TARGET"] ?? "native"}:${buildPlatform}:${process.arch}`,
1599
+ compiler: [process.env["SCRIPTC_CC"] ?? "clang"],
1600
+ nodeVersion: process.version,
1601
+ implementation: await libraryFrontendImplementationFingerprint(),
1602
+ };
1603
+ const earlyHit = await readEarlyLibraryCache(cacheRoot, earlyCacheOptions, profile.sidecar === null ? undefined : profile.sidecar.path);
1604
+ if (earlyHit !== null) {
1605
+ timing("early-cache-hit");
1606
+ await compileLibraryNative(profile, earlyHit.cPath, archivePath, opts.sanitize ?? false, earlyHit.native);
1607
+ timing("native-archive");
1608
+ timing("complete");
1609
+ return {
1610
+ ok: true,
1611
+ archivePath,
1612
+ cPath: earlyHit.cPath,
1613
+ backend: earlyHit.native.backend,
1614
+ ...(earlyHit.irPath !== undefined ? { irPath: earlyHit.irPath } : {}),
1615
+ ...(earlyHit.sidecarPath !== undefined ? { sidecarPath: earlyHit.sidecarPath } : {}),
1616
+ };
1617
+ }
1618
+ timing("early-cache-miss");
1619
+ const semanticHit = await readSemanticLibraryCache(cacheRoot, earlyCacheOptions, profile.sidecar === null ? undefined : profile.sidecar.path);
1620
+ if (semanticHit !== null) {
1621
+ timing("semantic-cache-hit", { changed_sources: semanticHit.changedSources.length });
1622
+ return emitSemanticLibraryHit(semanticHit, profile, opts, archivePath, cacheRoot, earlyCacheOptions, timing);
1623
+ }
1624
+ timing("semantic-cache-miss");
1286
1625
  // Bare npm specifiers in a library graph take the STATIC-OR-REFUSE
1287
1626
  // posture: "lib" runs the same auto-detection and eligibility bar as
1288
1627
  // the executable lane's --npm-static (own .d.ts, unminified shipped JS,
@@ -1445,9 +1784,9 @@ export async function compileLibrary(opts) {
1445
1784
  // The ask-2 contract sidecar rides the same invocation. Identity first
1446
1785
  // (schema §2's worked build_id definition over compiler version, profile
1447
1786
  // bytes, and the sorted canonical module graph; source_hash per the
1448
- // profile's "module-graph" contract) — the u64 lands on the IR so both
1449
- // backends emit the identity getters from the ONE value the sidecar
1450
- // records (V12's coherence by construction), then the projection into
1787
+ // profile's "module-graph" contract) — the u64 lands on the IR so native
1788
+ // archive assembly emits the identity getters from the ONE value the
1789
+ // sidecar records (V12's coherence by construction), then the projection into
1451
1790
  // the schema (declaration orders from the AST) and the V1–V14
1452
1791
  // self-check before anything is written.
1453
1792
  let sidecarJson = null;
@@ -1532,41 +1871,8 @@ export async function compileLibrary(opts) {
1532
1871
  irPath = join(opts.outDir, `${stem}.lib.ir.json`);
1533
1872
  await writeFile(irPath, serializeModule(mod));
1534
1873
  }
1535
- const archivePath = opts.outPath ?? join(opts.outDir, `${stem}.lib.a`);
1536
- // Multi-instance library mode: the profile-declared external surface —
1537
- // the mode-provided entries, the identity getters, and the export map —
1538
- // is exactly the set the localization step keeps global.
1539
- const localizeSymbols = profile.localizeRuntime
1540
- ? [
1541
- profile.initSymbol,
1542
- profile.sinkRegisterSymbol,
1543
- ...(profile.collectSymbol !== null ? [profile.collectSymbol] : []),
1544
- ...(profile.resultResetSymbol !== null ? [profile.resultResetSymbol] : []),
1545
- ...(profile.callbackRegisterSymbol !== null ? [profile.callbackRegisterSymbol] : []),
1546
- ...(profile.sidecar !== null
1547
- ? [profile.sidecar.buildIdSymbol, profile.sidecar.abiVersionSymbol]
1548
- : []),
1549
- ...profile.exports.map((e) => e.symbol),
1550
- ]
1551
- : undefined;
1552
- await compileLibArchive({
1553
- cPath,
1554
- outPath: archivePath,
1555
- cacheIdentity: "scriptc-generated-library-v1",
1556
- sanitize: opts.sanitize ?? false,
1557
- optimization: profile.optimization,
1558
- ...(localizeSymbols !== undefined ? { localizeSymbols } : {}),
1559
- ...(profile.instancePerThread ? { threadInstances: true } : {}),
1560
- regex: moduleUsesRegex(mod),
1561
- assert: moduleUsesAssert(mod),
1562
- inspect: moduleUsesInspect(mod),
1563
- symbol: moduleUsesSymbol(mod),
1564
- searchParams: moduleUsesSearchParams(mod),
1565
- emitter: moduleUsesEmitter(mod),
1566
- zlib: moduleUsesZlib(mod),
1567
- copying: moduleUsesCopying(mod),
1568
- textDecoderLegacy: moduleUsesLegacyTextDecoder(mod),
1569
- });
1874
+ const nativeFeatures = libraryNativeFeatures(mod, profile.emission);
1875
+ await compileLibraryNative(profile, cPath, archivePath, opts.sanitize ?? false, nativeFeatures);
1570
1876
  timing("native-archive");
1571
1877
  // The sidecar lands beside the compiled object, written by the same
1572
1878
  // invocation (profile-declared name; the neutral default when the
@@ -1579,6 +1885,17 @@ export async function compileLibrary(opts) {
1579
1885
  : `${archivePath}.contract.json`;
1580
1886
  await writeFile(sidecarPath, sidecarJson);
1581
1887
  }
1888
+ const earlyPublish = {
1889
+ cPath,
1890
+ native: nativeFeatures,
1891
+ frontend: frontendInputs.snapshot(),
1892
+ semantic: { mod, sources: sourceTexts },
1893
+ ...(irPath !== undefined ? { irPath } : {}),
1894
+ ...(sidecarPath !== undefined ? { sidecarPath } : {}),
1895
+ };
1896
+ await publishEarlyLibraryCache(cacheRoot, earlyCacheOptions, earlyPublish).catch(() => undefined);
1897
+ await pruneBuildCache(cacheRoot);
1898
+ timing("early-cache-publish");
1582
1899
  timing("complete");
1583
1900
  return {
1584
1901
  ok: true,