@yetanotheraryan/coldstart 1.0.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.
@@ -0,0 +1,438 @@
1
+ "use strict";
2
+
3
+ // register.ts
4
+ var import_fs = require("fs");
5
+ var import_path = require("path");
6
+ var import_url = require("url");
7
+ var import_worker_threads = require("worker_threads");
8
+ var import_node_module = require("module");
9
+
10
+ // src/cjs.ts
11
+ var import_module = require("module");
12
+
13
+ // src/tracer.ts
14
+ var import_perf_hooks = require("perf_hooks");
15
+ var Tracer = class {
16
+ events = [];
17
+ startTime = performance.now();
18
+ histogram = null;
19
+ constructor() {
20
+ try {
21
+ this.histogram = (0, import_perf_hooks.monitorEventLoopDelay)({ resolution: 5 });
22
+ this.histogram.enable();
23
+ } catch {
24
+ this.histogram = null;
25
+ }
26
+ }
27
+ /**
28
+ * Called by cjs.ts for every require() interception.
29
+ * Appends to the flat event list — tree is built lazily on report().
30
+ */
31
+ record(event) {
32
+ this.events.push(event);
33
+ }
34
+ /**
35
+ * Mark startup as complete and return the full report.
36
+ * Call this after your server/app signals it's ready.
37
+ */
38
+ report() {
39
+ const totalStartupMs = performance.now() - this.startTime;
40
+ if (this.histogram) {
41
+ this.histogram.disable();
42
+ }
43
+ const tree = this.buildTree();
44
+ const flat = this.flatten(tree).sort((a, b) => b.inclusiveMs - a.inclusiveMs);
45
+ const slowest = [...flat].filter((n) => !n.cached && !n.isBuiltin).sort((a, b) => b.exclusiveMs - a.exclusiveMs).slice(0, 10);
46
+ const nodeModuleTime = flat.filter((n) => n.isNodeModule && !n.cached).reduce((sum, n) => sum + n.exclusiveMs, 0);
47
+ const firstPartyTime = flat.filter((n) => !n.isNodeModule && !n.isBuiltin && !n.cached).reduce((sum, n) => sum + n.exclusiveMs, 0);
48
+ return {
49
+ totalStartupMs,
50
+ eventLoop: {
51
+ maxBlockMs: this.histogram ? this.histogram.max / 1e6 : 0,
52
+ meanBlockMs: this.histogram ? this.histogram.mean / 1e6 : 0,
53
+ p99BlockMs: this.histogram ? this.histogram.percentile(99) / 1e6 : 0
54
+ },
55
+ tree,
56
+ flat,
57
+ slowest,
58
+ nodeModuleTime,
59
+ firstPartyTime,
60
+ totalModulesLoaded: this.events.length,
61
+ cachedModulesCount: this.events.filter((e) => e.cached).length
62
+ };
63
+ }
64
+ /**
65
+ * Build the parent→child tree from the flat event list.
66
+ *
67
+ * Strategy:
68
+ * 1. Create a ModuleNode for each event
69
+ * 2. Wire children to parents using parentPath
70
+ * 3. Compute exclusive time = inclusive - sum(children inclusive)
71
+ * 4. Assign depth via BFS from roots
72
+ */
73
+ buildTree() {
74
+ const nodes = this.events.map((e) => ({
75
+ id: e.id,
76
+ request: e.request,
77
+ resolvedPath: e.resolvedPath,
78
+ parentPath: e.parentPath,
79
+ inclusiveMs: e.durationMs,
80
+ exclusiveMs: e.durationMs,
81
+ // will be adjusted below
82
+ cached: e.cached,
83
+ isNodeModule: e.isNodeModule,
84
+ isBuiltin: e.isBuiltin,
85
+ children: [],
86
+ depth: 0
87
+ }));
88
+ const byPath = /* @__PURE__ */ new Map();
89
+ for (const node of nodes) {
90
+ const existing = byPath.get(node.resolvedPath) ?? [];
91
+ existing.push(node);
92
+ byPath.set(node.resolvedPath, existing);
93
+ }
94
+ const roots = [];
95
+ for (const node of nodes) {
96
+ if (node.parentPath === "<entry>") {
97
+ roots.push(node);
98
+ continue;
99
+ }
100
+ const parentNodes = byPath.get(node.parentPath);
101
+ if (parentNodes && parentNodes.length > 0) {
102
+ parentNodes[parentNodes.length - 1].children.push(node);
103
+ } else {
104
+ roots.push(node);
105
+ }
106
+ }
107
+ const computeExclusive = (node) => {
108
+ for (const child of node.children) {
109
+ computeExclusive(child);
110
+ }
111
+ const childrenInclusiveSum = node.children.reduce(
112
+ (sum, c) => sum + c.inclusiveMs,
113
+ 0
114
+ );
115
+ node.exclusiveMs = Math.max(0, node.inclusiveMs - childrenInclusiveSum);
116
+ };
117
+ for (const root of roots) {
118
+ computeExclusive(root);
119
+ }
120
+ const assignDepth = (node, depth) => {
121
+ node.depth = depth;
122
+ for (const child of node.children) {
123
+ assignDepth(child, depth + 1);
124
+ }
125
+ };
126
+ for (const root of roots) {
127
+ assignDepth(root, 0);
128
+ }
129
+ return roots;
130
+ }
131
+ /**
132
+ * Flatten the tree into a list (pre-order DFS).
133
+ */
134
+ flatten(nodes) {
135
+ const result = [];
136
+ const visit = (node) => {
137
+ result.push(node);
138
+ for (const child of node.children) {
139
+ visit(child);
140
+ }
141
+ };
142
+ for (const node of nodes) {
143
+ visit(node);
144
+ }
145
+ return result;
146
+ }
147
+ /**
148
+ * Reset — useful for testing multiple startups in the same process.
149
+ */
150
+ reset() {
151
+ this.events = [];
152
+ this.startTime = performance.now();
153
+ if (this.histogram) {
154
+ this.histogram.reset();
155
+ this.histogram.enable();
156
+ }
157
+ }
158
+ /** Expose raw events for testing */
159
+ getRawEvents() {
160
+ return [...this.events];
161
+ }
162
+ };
163
+ var tracer = new Tracer();
164
+
165
+ // src/cjs.ts
166
+ var originalLoad = import_module.Module._load;
167
+ var callStack = [];
168
+ import_module.Module._load = function coldstartLoad(request, parent, isMain) {
169
+ let resolvedFilename;
170
+ try {
171
+ resolvedFilename = import_module.Module._resolveFilename(request, parent, isMain);
172
+ } catch {
173
+ return originalLoad.apply(this, arguments);
174
+ }
175
+ const isCached = !!import_module.Module._cache[resolvedFilename];
176
+ const isBuiltin = import_module.Module.isBuiltin?.(request) ?? isBuiltinModule(request);
177
+ const parentFilename = callStack.length > 0 ? callStack[callStack.length - 1] : parent?.filename ?? "<entry>";
178
+ callStack.push(resolvedFilename);
179
+ const startTime = performance.now();
180
+ let result;
181
+ try {
182
+ result = originalLoad.apply(this, arguments);
183
+ } finally {
184
+ callStack.pop();
185
+ if (!isBuiltin) {
186
+ const duration = performance.now() - startTime;
187
+ tracer.record({
188
+ id: resolvedFilename,
189
+ request,
190
+ // original string passed to require()
191
+ resolvedPath: resolvedFilename,
192
+ parentPath: parentFilename,
193
+ durationMs: duration,
194
+ startTime,
195
+ cached: isCached,
196
+ isNodeModule: resolvedFilename.includes("node_modules"),
197
+ isBuiltin: false
198
+ });
199
+ }
200
+ }
201
+ return result;
202
+ };
203
+ var originalResolveFilename = import_module.Module._resolveFilename;
204
+ import_module.Module._resolveFilename = function coldstartResolve(request, parent, isMain, options) {
205
+ if (import_module.Module.isBuiltin?.(request) ?? isBuiltinModule(request)) {
206
+ const parentFilename = callStack.length > 0 ? callStack[callStack.length - 1] : parent?.filename ?? "<entry>";
207
+ tracer.record({
208
+ id: `builtin:${request}`,
209
+ request,
210
+ resolvedPath: `builtin:${request}`,
211
+ parentPath: parentFilename,
212
+ durationMs: 0,
213
+ startTime: performance.now(),
214
+ cached: true,
215
+ isNodeModule: false,
216
+ isBuiltin: true
217
+ });
218
+ }
219
+ return originalResolveFilename.apply(this, arguments);
220
+ };
221
+ function isBuiltinModule(name) {
222
+ const builtins = /* @__PURE__ */ new Set([
223
+ "assert",
224
+ "async_hooks",
225
+ "buffer",
226
+ "child_process",
227
+ "cluster",
228
+ "console",
229
+ "constants",
230
+ "crypto",
231
+ "dgram",
232
+ "diagnostics_channel",
233
+ "dns",
234
+ "domain",
235
+ "events",
236
+ "fs",
237
+ "http",
238
+ "http2",
239
+ "https",
240
+ "inspector",
241
+ "module",
242
+ "net",
243
+ "os",
244
+ "path",
245
+ "perf_hooks",
246
+ "process",
247
+ "punycode",
248
+ "querystring",
249
+ "readline",
250
+ "repl",
251
+ "stream",
252
+ "string_decoder",
253
+ "sys",
254
+ "timers",
255
+ "tls",
256
+ "trace_events",
257
+ "tty",
258
+ "url",
259
+ "util",
260
+ "v8",
261
+ "vm",
262
+ "wasi",
263
+ "worker_threads",
264
+ "zlib"
265
+ ]);
266
+ const stripped = name.startsWith("node:") ? name.slice(5) : name;
267
+ return builtins.has(stripped);
268
+ }
269
+
270
+ // register.ts
271
+ var ESM_EXIT_SYMBOL = /* @__PURE__ */ Symbol.for("coldstart:esm:exit");
272
+ var COLDSTART_REGISTERED = /* @__PURE__ */ Symbol.for("coldstart:registered");
273
+ var COLDSTART_ESM_PORT = /* @__PURE__ */ Symbol.for("coldstart:esm:port");
274
+ var reportFile = process.env.COLDSTART_REPORT_FILE;
275
+ if (!globalThis[COLDSTART_REGISTERED]) {
276
+ ;
277
+ globalThis[COLDSTART_REGISTERED] = true;
278
+ setupEsmHooks();
279
+ }
280
+ if (reportFile) {
281
+ let flushed = false;
282
+ let beforeExitScheduled = false;
283
+ const flushReport = () => {
284
+ if (flushed) {
285
+ return;
286
+ }
287
+ flushed = true;
288
+ try {
289
+ (0, import_fs.mkdirSync)((0, import_path.dirname)(reportFile), { recursive: true });
290
+ (0, import_fs.writeFileSync)(reportFile, JSON.stringify(tracer.report(), jsonReplacer), "utf8");
291
+ } catch {
292
+ }
293
+ closeEsmPort();
294
+ };
295
+ process.once("beforeExit", () => {
296
+ if (beforeExitScheduled) {
297
+ return;
298
+ }
299
+ beforeExitScheduled = true;
300
+ setImmediate(flushReport);
301
+ });
302
+ process.once("exit", flushReport);
303
+ process.once("SIGINT", () => {
304
+ flushReport();
305
+ process.exit(130);
306
+ });
307
+ process.once("SIGTERM", () => {
308
+ flushReport();
309
+ process.exit(143);
310
+ });
311
+ process.once("uncaughtException", (error) => {
312
+ flushReport();
313
+ throw error;
314
+ });
315
+ }
316
+ function jsonReplacer(_key, value) {
317
+ if (typeof value === "number" && !Number.isFinite(value)) {
318
+ return 0;
319
+ }
320
+ return value;
321
+ }
322
+ function setupEsmHooks() {
323
+ if (typeof import_node_module.register !== "function") {
324
+ return;
325
+ }
326
+ const { port1, port2 } = new import_worker_threads.MessageChannel();
327
+ const pendingByUrl = /* @__PURE__ */ new Map();
328
+ const completedAtByUrl = /* @__PURE__ */ new Map();
329
+ const finalizedUrls = /* @__PURE__ */ new Set();
330
+ let closeTimer = null;
331
+ globalThis[COLDSTART_ESM_PORT] = port1;
332
+ port1.unref();
333
+ const schedulePortClose = () => {
334
+ if (closeTimer) {
335
+ clearTimeout(closeTimer);
336
+ }
337
+ closeTimer = setTimeout(() => {
338
+ closeTimer = null;
339
+ closeEsmPort();
340
+ }, 25);
341
+ };
342
+ if (!reportFile) {
343
+ process.once("beforeExit", () => {
344
+ setImmediate(closeEsmPort);
345
+ });
346
+ }
347
+ process.once("exit", closeEsmPort);
348
+ port1.on("message", (rawMessage) => {
349
+ schedulePortClose();
350
+ if (!isEsmResolveMessage(rawMessage)) {
351
+ return;
352
+ }
353
+ const event = {
354
+ id: rawMessage.isBuiltin ? `builtin:${rawMessage.request}` : rawMessage.resolvedPath,
355
+ request: rawMessage.request,
356
+ resolvedPath: rawMessage.resolvedPath,
357
+ parentPath: rawMessage.parentPath,
358
+ durationMs: 0,
359
+ startTime: rawMessage.startTime,
360
+ cached: rawMessage.isBuiltin,
361
+ isNodeModule: rawMessage.isNodeModule,
362
+ isBuiltin: rawMessage.isBuiltin
363
+ };
364
+ if (event.isBuiltin) {
365
+ tracer.record(event);
366
+ return;
367
+ }
368
+ const completedAt = completedAtByUrl.get(event.resolvedPath);
369
+ if (typeof completedAt === "number" && !finalizedUrls.has(event.resolvedPath)) {
370
+ tracer.record({
371
+ ...event,
372
+ durationMs: Math.max(0, completedAt - event.startTime),
373
+ cached: false
374
+ });
375
+ finalizedUrls.add(event.resolvedPath);
376
+ completedAtByUrl.delete(event.resolvedPath);
377
+ return;
378
+ }
379
+ if (finalizedUrls.has(event.resolvedPath)) {
380
+ tracer.record({
381
+ ...event,
382
+ startTime: performance.now(),
383
+ durationMs: 0,
384
+ cached: true
385
+ });
386
+ return;
387
+ }
388
+ const existing = pendingByUrl.get(event.resolvedPath) ?? [];
389
+ existing.push(event);
390
+ pendingByUrl.set(event.resolvedPath, existing);
391
+ });
392
+ globalThis[ESM_EXIT_SYMBOL] = (resolvedPath) => {
393
+ const endTime = performance.now();
394
+ const pending = pendingByUrl.get(resolvedPath);
395
+ schedulePortClose();
396
+ if (!pending || pending.length === 0) {
397
+ completedAtByUrl.set(resolvedPath, endTime);
398
+ return;
399
+ }
400
+ const [primary, ...rest] = pending;
401
+ tracer.record({
402
+ ...primary,
403
+ durationMs: Math.max(0, endTime - primary.startTime),
404
+ cached: false
405
+ });
406
+ for (const cachedEdge of rest) {
407
+ tracer.record({
408
+ ...cachedEdge,
409
+ startTime: endTime,
410
+ durationMs: 0,
411
+ cached: true
412
+ });
413
+ }
414
+ pendingByUrl.delete(resolvedPath);
415
+ completedAtByUrl.delete(resolvedPath);
416
+ finalizedUrls.add(resolvedPath);
417
+ };
418
+ try {
419
+ (0, import_node_module.register)("./esm-loader.mjs", {
420
+ parentURL: (0, import_url.pathToFileURL)(__filename),
421
+ data: { port: port2 },
422
+ transferList: [port2]
423
+ });
424
+ schedulePortClose();
425
+ } catch {
426
+ }
427
+ }
428
+ function closeEsmPort() {
429
+ const port = globalThis[COLDSTART_ESM_PORT];
430
+ try {
431
+ port?.close?.();
432
+ } catch {
433
+ }
434
+ delete globalThis[COLDSTART_ESM_PORT];
435
+ }
436
+ function isEsmResolveMessage(value) {
437
+ return typeof value === "object" && value !== null && value.type === "resolve";
438
+ }
@@ -0,0 +1,181 @@
1
+ import {
2
+ tracer
3
+ } from "./chunk-7HD2T2AD.mjs";
4
+ import {
5
+ __filename
6
+ } from "./chunk-B5TADGOF.mjs";
7
+
8
+ // register.ts
9
+ import { mkdirSync, writeFileSync } from "fs";
10
+ import { dirname } from "path";
11
+ import { pathToFileURL } from "url";
12
+ import { MessageChannel } from "worker_threads";
13
+ import { register as registerHooks } from "module";
14
+ var ESM_EXIT_SYMBOL = /* @__PURE__ */ Symbol.for("coldstart:esm:exit");
15
+ var COLDSTART_REGISTERED = /* @__PURE__ */ Symbol.for("coldstart:registered");
16
+ var COLDSTART_ESM_PORT = /* @__PURE__ */ Symbol.for("coldstart:esm:port");
17
+ var reportFile = process.env.COLDSTART_REPORT_FILE;
18
+ if (!globalThis[COLDSTART_REGISTERED]) {
19
+ ;
20
+ globalThis[COLDSTART_REGISTERED] = true;
21
+ setupEsmHooks();
22
+ }
23
+ if (reportFile) {
24
+ let flushed = false;
25
+ let beforeExitScheduled = false;
26
+ const flushReport = () => {
27
+ if (flushed) {
28
+ return;
29
+ }
30
+ flushed = true;
31
+ try {
32
+ mkdirSync(dirname(reportFile), { recursive: true });
33
+ writeFileSync(reportFile, JSON.stringify(tracer.report(), jsonReplacer), "utf8");
34
+ } catch {
35
+ }
36
+ closeEsmPort();
37
+ };
38
+ process.once("beforeExit", () => {
39
+ if (beforeExitScheduled) {
40
+ return;
41
+ }
42
+ beforeExitScheduled = true;
43
+ setImmediate(flushReport);
44
+ });
45
+ process.once("exit", flushReport);
46
+ process.once("SIGINT", () => {
47
+ flushReport();
48
+ process.exit(130);
49
+ });
50
+ process.once("SIGTERM", () => {
51
+ flushReport();
52
+ process.exit(143);
53
+ });
54
+ process.once("uncaughtException", (error) => {
55
+ flushReport();
56
+ throw error;
57
+ });
58
+ }
59
+ function jsonReplacer(_key, value) {
60
+ if (typeof value === "number" && !Number.isFinite(value)) {
61
+ return 0;
62
+ }
63
+ return value;
64
+ }
65
+ function setupEsmHooks() {
66
+ if (typeof registerHooks !== "function") {
67
+ return;
68
+ }
69
+ const { port1, port2 } = new MessageChannel();
70
+ const pendingByUrl = /* @__PURE__ */ new Map();
71
+ const completedAtByUrl = /* @__PURE__ */ new Map();
72
+ const finalizedUrls = /* @__PURE__ */ new Set();
73
+ let closeTimer = null;
74
+ globalThis[COLDSTART_ESM_PORT] = port1;
75
+ port1.unref();
76
+ const schedulePortClose = () => {
77
+ if (closeTimer) {
78
+ clearTimeout(closeTimer);
79
+ }
80
+ closeTimer = setTimeout(() => {
81
+ closeTimer = null;
82
+ closeEsmPort();
83
+ }, 25);
84
+ };
85
+ if (!reportFile) {
86
+ process.once("beforeExit", () => {
87
+ setImmediate(closeEsmPort);
88
+ });
89
+ }
90
+ process.once("exit", closeEsmPort);
91
+ port1.on("message", (rawMessage) => {
92
+ schedulePortClose();
93
+ if (!isEsmResolveMessage(rawMessage)) {
94
+ return;
95
+ }
96
+ const event = {
97
+ id: rawMessage.isBuiltin ? `builtin:${rawMessage.request}` : rawMessage.resolvedPath,
98
+ request: rawMessage.request,
99
+ resolvedPath: rawMessage.resolvedPath,
100
+ parentPath: rawMessage.parentPath,
101
+ durationMs: 0,
102
+ startTime: rawMessage.startTime,
103
+ cached: rawMessage.isBuiltin,
104
+ isNodeModule: rawMessage.isNodeModule,
105
+ isBuiltin: rawMessage.isBuiltin
106
+ };
107
+ if (event.isBuiltin) {
108
+ tracer.record(event);
109
+ return;
110
+ }
111
+ const completedAt = completedAtByUrl.get(event.resolvedPath);
112
+ if (typeof completedAt === "number" && !finalizedUrls.has(event.resolvedPath)) {
113
+ tracer.record({
114
+ ...event,
115
+ durationMs: Math.max(0, completedAt - event.startTime),
116
+ cached: false
117
+ });
118
+ finalizedUrls.add(event.resolvedPath);
119
+ completedAtByUrl.delete(event.resolvedPath);
120
+ return;
121
+ }
122
+ if (finalizedUrls.has(event.resolvedPath)) {
123
+ tracer.record({
124
+ ...event,
125
+ startTime: performance.now(),
126
+ durationMs: 0,
127
+ cached: true
128
+ });
129
+ return;
130
+ }
131
+ const existing = pendingByUrl.get(event.resolvedPath) ?? [];
132
+ existing.push(event);
133
+ pendingByUrl.set(event.resolvedPath, existing);
134
+ });
135
+ globalThis[ESM_EXIT_SYMBOL] = (resolvedPath) => {
136
+ const endTime = performance.now();
137
+ const pending = pendingByUrl.get(resolvedPath);
138
+ schedulePortClose();
139
+ if (!pending || pending.length === 0) {
140
+ completedAtByUrl.set(resolvedPath, endTime);
141
+ return;
142
+ }
143
+ const [primary, ...rest] = pending;
144
+ tracer.record({
145
+ ...primary,
146
+ durationMs: Math.max(0, endTime - primary.startTime),
147
+ cached: false
148
+ });
149
+ for (const cachedEdge of rest) {
150
+ tracer.record({
151
+ ...cachedEdge,
152
+ startTime: endTime,
153
+ durationMs: 0,
154
+ cached: true
155
+ });
156
+ }
157
+ pendingByUrl.delete(resolvedPath);
158
+ completedAtByUrl.delete(resolvedPath);
159
+ finalizedUrls.add(resolvedPath);
160
+ };
161
+ try {
162
+ registerHooks("./esm-loader.mjs", {
163
+ parentURL: pathToFileURL(__filename),
164
+ data: { port: port2 },
165
+ transferList: [port2]
166
+ });
167
+ schedulePortClose();
168
+ } catch {
169
+ }
170
+ }
171
+ function closeEsmPort() {
172
+ const port = globalThis[COLDSTART_ESM_PORT];
173
+ try {
174
+ port?.close?.();
175
+ } catch {
176
+ }
177
+ delete globalThis[COLDSTART_ESM_PORT];
178
+ }
179
+ function isEsmResolveMessage(value) {
180
+ return typeof value === "object" && value !== null && value.type === "resolve";
181
+ }
package/package.json ADDED
@@ -0,0 +1,63 @@
1
+ {
2
+ "name": "@yetanotheraryan/coldstart",
3
+ "version": "1.0.0",
4
+ "description": "Profile Node.js startup by timing every require() and reporting slow modules.",
5
+ "main": "./dist/index.js",
6
+ "module": "./dist/index.mjs",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "require": "./dist/index.js",
12
+ "import": "./dist/index.mjs",
13
+ "default": "./dist/index.js"
14
+ },
15
+ "./register": {
16
+ "types": "./dist/register.d.ts",
17
+ "require": "./dist/register.js",
18
+ "import": "./dist/register.mjs",
19
+ "default": "./dist/register.js"
20
+ },
21
+ "./package.json": "./package.json"
22
+ },
23
+ "bin": {
24
+ "coldstart": "./dist/cli.js"
25
+ },
26
+ "files": [
27
+ "dist",
28
+ "readme.md",
29
+ "LICENSE"
30
+ ],
31
+ "keywords": [
32
+ "nodejs",
33
+ "startup",
34
+ "profiling",
35
+ "performance",
36
+ "require",
37
+ "commonjs",
38
+ "tracing",
39
+ "cli"
40
+ ],
41
+ "engines": {
42
+ "node": ">=18"
43
+ },
44
+ "scripts": {
45
+ "build": "tsup",
46
+ "typecheck": "tsc -p tsconfig.json"
47
+ },
48
+ "repository": {
49
+ "type": "git",
50
+ "url": "git+https://github.com/yetanotheraryan/coldstart.git"
51
+ },
52
+ "author": "Aryan Tiwari",
53
+ "license": "MIT",
54
+ "bugs": {
55
+ "url": "https://github.com/yetanotheraryan/coldstart/issues"
56
+ },
57
+ "homepage": "https://github.com/yetanotheraryan/coldstart#readme",
58
+ "devDependencies": {
59
+ "@types/node": "^22.15.3",
60
+ "tsup": "^8.5.1",
61
+ "typescript": "^5.9.3"
62
+ }
63
+ }