@interfere/build 0.2.2 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -6,3 +6,20 @@ Use `@interfere/javascript` to call release endpoints directly. This package
6
6
  provides build configuration resolution, release metadata, ordered source-map
7
7
  chunk uploads, and local artifact cleanup. Framework integrations own the sequence
8
8
  of API calls and only delete source maps after the server accepts the full batch.
9
+
10
+ Source-map uploads use 20 concurrent workers by default. Set
11
+ `INTERFERE_SOURCEMAP_UPLOAD_CONCURRENCY` to a positive integer to change the limit;
12
+ the `uploadSourceMapChunks` `concurrency` option takes precedence. Next.js and Vite
13
+ share this setting. Pass the variable through your build runner when it filters
14
+ environment variables, such as Turborepo's strict environment mode.
15
+
16
+ Next.js and Vite use gzip only when the signed upload session advertises support,
17
+ with identity uploads for payloads that do not shrink. Older servers receive
18
+ uncompressed chunks of at most 512 KiB. Servers advertising gzip accept chunks
19
+ of up to 8 MiB. Chunk offsets and reported byte counts refer to the original,
20
+ uncompressed content.
21
+
22
+ When calling `uploadSourceMapChunks` directly, construct the API client with
23
+ `fetch: sourceMapUploadFetch` from `@interfere/build/source-map-upload-fetch` and
24
+ pass `compression: "gzip"` to the uploader. Direct callers retain identity uploads
25
+ unless they opt in; Next.js and Vite configure both automatically.
@@ -0,0 +1 @@
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});let node_util=require("node:util"),node_zlib=require("node:zlib");const compress=(0,node_util.promisify)(node_zlib.gzip),sourceMapUploadFetch=Object.assign(async(input,init)=>{let request=new Request(input,init);if(request.headers.get(`content-encoding`)!==`gzip`)return fetch(request);let bytes=await request.arrayBuffer(),body=await compress(bytes),headers=new Headers(request.headers);return headers.delete(`content-length`),body.byteLength>=bytes.byteLength?(headers.delete(`content-encoding`),fetch(new Request(request,{body:bytes,headers,method:request.method}))):fetch(new Request(request,{body,headers,method:request.method}))},fetch);exports.sourceMapUploadFetch=sourceMapUploadFetch;
@@ -0,0 +1,4 @@
1
+ //#region src/source-map-upload-fetch.d.ts
2
+ /** Compresses requests marked with Content-Encoding: gzip by the source-map uploader. */
3
+ export declare const sourceMapUploadFetch: typeof fetch;
4
+ //#endregion
@@ -0,0 +1,4 @@
1
+ //#region src/source-map-upload-fetch.d.ts
2
+ /** Compresses requests marked with Content-Encoding: gzip by the source-map uploader. */
3
+ export declare const sourceMapUploadFetch: typeof fetch;
4
+ //#endregion
@@ -0,0 +1 @@
1
+ import{promisify}from"node:util";import{gzip}from"node:zlib";const compress=promisify(gzip),sourceMapUploadFetch=Object.assign(async(input,init)=>{let request=new Request(input,init);if(request.headers.get(`content-encoding`)!==`gzip`)return fetch(request);let bytes=await request.arrayBuffer(),body=await compress(bytes),headers=new Headers(request.headers);return headers.delete(`content-length`),body.byteLength>=bytes.byteLength?(headers.delete(`content-encoding`),fetch(new Request(request,{body:bytes,headers,method:request.method}))):fetch(new Request(request,{body,headers,method:request.method}))},fetch);export{sourceMapUploadFetch};
@@ -1 +1 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const require_upload_source_map_batch=require("./upload-source-map-batch.cjs"),require_upload_source_map_request=require("./upload-source-map-request.cjs"),CHUNK_BYTES=524288;async function uploadSourceMapChunks(options){let{client,environment,files,releaseSlug,signed}=options,controller=new AbortController;try{let sessionDeadlineMs=signed.expiresAt*1e3,assertSessionOpen=()=>{if(!Number.isFinite(sessionDeadlineMs)||Date.now()>=sessionDeadlineMs)throw Error(`Source-map upload session expired`)};assertSessionOpen();let abortSignal=AbortSignal.any([controller.signal,AbortSignal.timeout(sessionDeadlineMs-Date.now())]),expected=new Set(files.map(file=>file.path));if(signed.uploads.length!==files.length||new Set(signed.uploads.map(file=>file.path)).size!==files.length||signed.uploads.some(file=>!expected.has(file.path)))throw Error(`Source-map server returned a different batch`);let jobs=require_upload_source_map_batch.sourceMapUploadJobs(files,signed.batchUpload),totalBytes=0;return await Promise.all(Array.from({length:Math.min(4,files.length)},async()=>{for(let job of jobs){if(abortSignal.throwIfAborted(),assertSessionOpen(),job.chunks){await require_upload_source_map_request.uploadSourceMapRequest(async()=>await client.releases.uploadSourceMapBatch({releaseSlug,environment,chunks:job.chunks},{abortSignal}),abortSignal),totalBytes+=job.rawBytes;continue}let{file}=job,bytes=new TextEncoder().encode(file.content);for(let offset=0;offset<bytes.byteLength;offset+=CHUNK_BYTES)assertSessionOpen(),await require_upload_source_map_request.uploadSourceMapRequest(async()=>await client.releases.uploadSourceMap(new Blob([bytes.slice(offset,offset+CHUNK_BYTES)]),releaseSlug,{environment,path:file.path,offset},{abortSignal}),abortSignal);totalBytes+=bytes.byteLength}})),assertSessionOpen(),totalBytes}finally{controller.abort()}}exports.uploadSourceMapChunks=uploadSourceMapChunks;
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const require_upload_source_map_batch=require("./upload-source-map-batch.cjs"),require_upload_source_map_request=require("./upload-source-map-request.cjs");let _interfere_types_data_source_map_upload_batch=require("@interfere/types/data/source-map-upload-batch");async function uploadSourceMapChunks(options){let{client,environment,files,releaseSlug,signed}=options,concurrency=options.concurrency??Number(process.env.INTERFERE_SOURCEMAP_UPLOAD_CONCURRENCY??20);if(!Number.isSafeInteger(concurrency)||concurrency<1)throw Error(`Source-map upload concurrency must be a positive integer`);let capability=_interfere_types_data_source_map_upload_batch.sourceMapBatchCapability.safeParse(signed.batchUpload),supportsGzip=capability.success&&capability.data.compression===`gzip`,chunkBytes=supportsGzip?8388608:524288,headers=options.compression===`gzip`&&supportsGzip?{"content-encoding":`gzip`}:void 0,controller=new AbortController;try{let sessionDeadlineMs=signed.expiresAt*1e3,assertSessionOpen=()=>{if(!Number.isFinite(sessionDeadlineMs)||Date.now()>=sessionDeadlineMs)throw Error(`Source-map upload session expired`)};assertSessionOpen();let abortSignal=AbortSignal.any([controller.signal,AbortSignal.timeout(sessionDeadlineMs-Date.now())]),expected=new Set(files.map(file=>file.path));if(signed.uploads.length!==files.length||new Set(signed.uploads.map(file=>file.path)).size!==files.length||signed.uploads.some(file=>!expected.has(file.path)))throw Error(`Source-map server returned a different batch`);let jobs=require_upload_source_map_batch.sourceMapUploadJobs(files,signed.batchUpload),totalBytes=0;return await Promise.all(Array.from({length:Math.min(concurrency,files.length)},async()=>{for(let job of jobs){if(abortSignal.throwIfAborted(),assertSessionOpen(),job.chunks){await require_upload_source_map_request.uploadSourceMapRequest(async()=>await client.releases.uploadSourceMapBatch({releaseSlug,environment,chunks:job.chunks},{abortSignal,headers}),abortSignal),totalBytes+=job.rawBytes;continue}let{file}=job,bytes=new TextEncoder().encode(file.content);for(let offset=0;offset<bytes.byteLength;offset+=chunkBytes){assertSessionOpen();let chunk=new Blob([bytes.subarray(offset,offset+chunkBytes)]);await require_upload_source_map_request.uploadSourceMapRequest(async()=>await client.releases.uploadSourceMap(chunk,releaseSlug,{environment,path:file.path,offset},{abortSignal,headers:{...headers,"x-interfere-source-map-size":String(chunk.size)}}),abortSignal)}totalBytes+=bytes.byteLength}})),assertSessionOpen(),totalBytes}finally{controller.abort()}}exports.uploadSourceMapChunks=uploadSourceMapChunks;
@@ -5,6 +5,9 @@ export declare function uploadSourceMapChunks(options: {
5
5
  releaseSlug: string;
6
6
  environment?: string | undefined;
7
7
  signed: Interfere.SourceMapUploadSessionResponse;
8
+ concurrency?: number;
9
+ /** Requires sourceMapUploadFetch on the client; defaults to identity. */
10
+ compression?: "gzip";
8
11
  files: readonly (Interfere.CompleteSourceMapFile & {
9
12
  content: string;
10
13
  })[];
@@ -5,6 +5,9 @@ export declare function uploadSourceMapChunks(options: {
5
5
  releaseSlug: string;
6
6
  environment?: string | undefined;
7
7
  signed: Interfere.SourceMapUploadSessionResponse;
8
+ concurrency?: number;
9
+ /** Requires sourceMapUploadFetch on the client; defaults to identity. */
10
+ compression?: "gzip";
8
11
  files: readonly (Interfere.CompleteSourceMapFile & {
9
12
  content: string;
10
13
  })[];
@@ -1 +1 @@
1
- import{sourceMapUploadJobs}from"./upload-source-map-batch.mjs";import{uploadSourceMapRequest}from"./upload-source-map-request.mjs";const CHUNK_BYTES=524288;async function uploadSourceMapChunks(options){let{client,environment,files,releaseSlug,signed}=options,controller=new AbortController;try{let sessionDeadlineMs=signed.expiresAt*1e3,assertSessionOpen=()=>{if(!Number.isFinite(sessionDeadlineMs)||Date.now()>=sessionDeadlineMs)throw Error(`Source-map upload session expired`)};assertSessionOpen();let abortSignal=AbortSignal.any([controller.signal,AbortSignal.timeout(sessionDeadlineMs-Date.now())]),expected=new Set(files.map(file=>file.path));if(signed.uploads.length!==files.length||new Set(signed.uploads.map(file=>file.path)).size!==files.length||signed.uploads.some(file=>!expected.has(file.path)))throw Error(`Source-map server returned a different batch`);let jobs=sourceMapUploadJobs(files,signed.batchUpload),totalBytes=0;return await Promise.all(Array.from({length:Math.min(4,files.length)},async()=>{for(let job of jobs){if(abortSignal.throwIfAborted(),assertSessionOpen(),job.chunks){await uploadSourceMapRequest(async()=>await client.releases.uploadSourceMapBatch({releaseSlug,environment,chunks:job.chunks},{abortSignal}),abortSignal),totalBytes+=job.rawBytes;continue}let{file}=job,bytes=new TextEncoder().encode(file.content);for(let offset=0;offset<bytes.byteLength;offset+=CHUNK_BYTES)assertSessionOpen(),await uploadSourceMapRequest(async()=>await client.releases.uploadSourceMap(new Blob([bytes.slice(offset,offset+CHUNK_BYTES)]),releaseSlug,{environment,path:file.path,offset},{abortSignal}),abortSignal);totalBytes+=bytes.byteLength}})),assertSessionOpen(),totalBytes}finally{controller.abort()}}export{uploadSourceMapChunks};
1
+ import{sourceMapUploadJobs}from"./upload-source-map-batch.mjs";import{uploadSourceMapRequest}from"./upload-source-map-request.mjs";import{sourceMapBatchCapability}from"@interfere/types/data/source-map-upload-batch";async function uploadSourceMapChunks(options){let{client,environment,files,releaseSlug,signed}=options,concurrency=options.concurrency??Number(process.env.INTERFERE_SOURCEMAP_UPLOAD_CONCURRENCY??20);if(!Number.isSafeInteger(concurrency)||concurrency<1)throw Error(`Source-map upload concurrency must be a positive integer`);let capability=sourceMapBatchCapability.safeParse(signed.batchUpload),supportsGzip=capability.success&&capability.data.compression===`gzip`,chunkBytes=supportsGzip?8388608:524288,headers=options.compression===`gzip`&&supportsGzip?{"content-encoding":`gzip`}:void 0,controller=new AbortController;try{let sessionDeadlineMs=signed.expiresAt*1e3,assertSessionOpen=()=>{if(!Number.isFinite(sessionDeadlineMs)||Date.now()>=sessionDeadlineMs)throw Error(`Source-map upload session expired`)};assertSessionOpen();let abortSignal=AbortSignal.any([controller.signal,AbortSignal.timeout(sessionDeadlineMs-Date.now())]),expected=new Set(files.map(file=>file.path));if(signed.uploads.length!==files.length||new Set(signed.uploads.map(file=>file.path)).size!==files.length||signed.uploads.some(file=>!expected.has(file.path)))throw Error(`Source-map server returned a different batch`);let jobs=sourceMapUploadJobs(files,signed.batchUpload),totalBytes=0;return await Promise.all(Array.from({length:Math.min(concurrency,files.length)},async()=>{for(let job of jobs){if(abortSignal.throwIfAborted(),assertSessionOpen(),job.chunks){await uploadSourceMapRequest(async()=>await client.releases.uploadSourceMapBatch({releaseSlug,environment,chunks:job.chunks},{abortSignal,headers}),abortSignal),totalBytes+=job.rawBytes;continue}let{file}=job,bytes=new TextEncoder().encode(file.content);for(let offset=0;offset<bytes.byteLength;offset+=chunkBytes){assertSessionOpen();let chunk=new Blob([bytes.subarray(offset,offset+chunkBytes)]);await uploadSourceMapRequest(async()=>await client.releases.uploadSourceMap(chunk,releaseSlug,{environment,path:file.path,offset},{abortSignal,headers:{...headers,"x-interfere-source-map-size":String(chunk.size)}}),abortSignal)}totalBytes+=bytes.byteLength}})),assertSessionOpen(),totalBytes}finally{controller.abort()}}export{uploadSourceMapChunks};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@interfere/build",
3
- "version": "0.2.2",
3
+ "version": "0.3.0",
4
4
  "description": "Release preparation for Interfere build integrations.",
5
5
  "keywords": [
6
6
  "build",
@@ -71,6 +71,17 @@
71
71
  "types": "./dist/upload-source-map-chunks.d.cts",
72
72
  "default": "./dist/upload-source-map-chunks.cjs"
73
73
  }
74
+ },
75
+ "./source-map-upload-fetch": {
76
+ "monorepo": "./src/source-map-upload-fetch.ts",
77
+ "import": {
78
+ "types": "./dist/source-map-upload-fetch.d.mts",
79
+ "default": "./dist/source-map-upload-fetch.mjs"
80
+ },
81
+ "require": {
82
+ "types": "./dist/source-map-upload-fetch.d.cts",
83
+ "default": "./dist/source-map-upload-fetch.cjs"
84
+ }
74
85
  }
75
86
  },
76
87
  "publishConfig": {
@@ -83,7 +94,7 @@
83
94
  },
84
95
  "dependencies": {
85
96
  "@interfere/javascript": "^0.2.1",
86
- "@interfere/types": "^10.2.1"
97
+ "@interfere/types": "^10.2.2"
87
98
  },
88
99
  "devDependencies": {
89
100
  "@interfere/typescript-config": "^9.0.2",