@solcreek/adapter-creek 0.1.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,240 @@
1
+ // Minimal node:http shim for CF Workers.
2
+ // Plain objects with the minimum API surface that Next.js handler needs.
3
+ // Does NOT extend from node:stream (CF Workers stream compat may be incomplete).
4
+
5
+ import { EventEmitter } from "node:events";
6
+
7
+ export class IncomingMessage extends EventEmitter {
8
+ constructor(socket) {
9
+ super();
10
+ this.socket = socket || { encrypted: true, remoteAddress: "127.0.0.1", address: () => ({ port: 443 }), end() {}, destroy() {} };
11
+ this.connection = this.socket;
12
+ this.httpVersion = "1.1";
13
+ this.httpVersionMajor = 1;
14
+ this.httpVersionMinor = 1;
15
+ this.complete = true;
16
+ this.headers = {};
17
+ this.rawHeaders = [];
18
+ this.trailers = {};
19
+ this.rawTrailers = [];
20
+ this.method = "GET";
21
+ this.url = "/";
22
+ this.statusCode = null;
23
+ this.statusMessage = null;
24
+ this.aborted = false;
25
+ this.upgrade = false;
26
+ this.readable = true;
27
+ this._body = null;
28
+ this._bodyConsumed = false;
29
+ // Node.js Readable state — some Next.js code checks this directly.
30
+ this._readableState = { ended: false, endEmitted: false, flowing: null };
31
+ // Buffer chunks until a listener is attached.
32
+ // push() may be called before the handler adds "data" listeners.
33
+ this._bufferedChunks = [];
34
+ this._ended = false;
35
+ this._flowing = false;
36
+ }
37
+ // Readable stream interface (minimal)
38
+ read() {
39
+ if (this._bufferedChunks.length > 0) return this._bufferedChunks.shift();
40
+ return null;
41
+ }
42
+ push(chunk) {
43
+ if (chunk === null) {
44
+ this._ended = true;
45
+ this.complete = true;
46
+ this._readableState.ended = true;
47
+ if (this._flowing) {
48
+ this._readableState.endEmitted = true;
49
+ this.emit("end");
50
+ }
51
+ return;
52
+ }
53
+ if (this._flowing) {
54
+ this.emit("data", chunk);
55
+ } else {
56
+ this._bufferedChunks.push(chunk);
57
+ }
58
+ }
59
+ // Flush buffered data when listeners are ready
60
+ _startFlowing() {
61
+ if (this._flowing) return;
62
+ this._flowing = true;
63
+ this._readableState.flowing = true;
64
+ while (this._bufferedChunks.length > 0) {
65
+ this.emit("data", this._bufferedChunks.shift());
66
+ }
67
+ if (this._ended) {
68
+ this._readableState.endEmitted = true;
69
+ this.emit("end");
70
+ }
71
+ }
72
+ on(event, fn) {
73
+ super.on(event, fn);
74
+ // When a "data" listener is attached, start flowing
75
+ if (event === "data" && !this._flowing) {
76
+ queueMicrotask(() => this._startFlowing());
77
+ }
78
+ return this;
79
+ }
80
+ addListener(event, fn) { return this.on(event, fn); }
81
+ pipe(dest) {
82
+ this.on("data", (chunk) => dest.write(chunk));
83
+ this.on("end", () => { if (dest.end) dest.end(); });
84
+ return dest;
85
+ }
86
+ unpipe() {}
87
+ resume() { this._startFlowing(); return this; }
88
+ pause() { return this; }
89
+ setEncoding() { return this; }
90
+ setTimeout() { return this; }
91
+ destroy() { this.emit("close"); return this; }
92
+ [Symbol.asyncIterator]() {
93
+ const self = this;
94
+ // Drain both currently-buffered chunks AND any chunks that arrive
95
+ // via subsequent push() calls. Use a pull-based queue with a
96
+ // resolver so we wake up the awaiting consumer exactly when new
97
+ // data arrives or end is reached. This replaces an older
98
+ // implementation that went through on("data"/"end") events with a
99
+ // queueMicrotask + _startFlowing dance — on workerd the microtask
100
+ // timing was unreliable for bodyParser:false handlers that iterate
101
+ // the request body directly (they would hang forever).
102
+ const queue = [];
103
+ let ended = false;
104
+ let resolver = null;
105
+ const notify = () => {
106
+ if (resolver) {
107
+ const r = resolver;
108
+ resolver = null;
109
+ r();
110
+ }
111
+ };
112
+ // Seed the queue with any pre-buffered chunks. Clear the shim's
113
+ // buffer so a later push() goes straight into our queue.
114
+ for (const chunk of self._bufferedChunks) queue.push(chunk);
115
+ self._bufferedChunks = [];
116
+ self._flowing = true;
117
+ self._readableState.flowing = true;
118
+ if (self._ended) {
119
+ ended = true;
120
+ self._readableState.endEmitted = true;
121
+ }
122
+ // Replace push() so later chunks land in our queue. Keep the null
123
+ // sentinel semantics for end-of-stream.
124
+ const origPush = self.push.bind(self);
125
+ self.push = (chunk) => {
126
+ if (chunk === null) {
127
+ ended = true;
128
+ self._ended = true;
129
+ self.complete = true;
130
+ self._readableState.ended = true;
131
+ self._readableState.endEmitted = true;
132
+ notify();
133
+ return;
134
+ }
135
+ queue.push(chunk);
136
+ notify();
137
+ };
138
+ return {
139
+ async next() {
140
+ if (queue.length === 0 && !ended) {
141
+ await new Promise((r) => { resolver = r; });
142
+ }
143
+ if (queue.length > 0) return { done: false, value: queue.shift() };
144
+ return { done: true, value: undefined };
145
+ }
146
+ };
147
+ }
148
+ }
149
+
150
+ export class ServerResponse extends EventEmitter {
151
+ constructor(req) {
152
+ super();
153
+ this.req = req;
154
+ this.statusCode = 200;
155
+ this.statusMessage = "";
156
+ this.headersSent = false;
157
+ this.finished = false;
158
+ this.writable = true;
159
+ this.sendDate = true;
160
+ this._headers = {};
161
+ this._headerNames = {};
162
+ this.socket = req?.socket || { encrypted: true, remoteAddress: "127.0.0.1" };
163
+ this.connection = this.socket;
164
+ }
165
+ setHeader(name, value) { this._headers[name.toLowerCase()] = value; this._headerNames[name.toLowerCase()] = name; }
166
+ appendHeader(name, value) {
167
+ const key = name.toLowerCase();
168
+ const existing = this._headers[key];
169
+ if (existing === undefined) {
170
+ this._headers[key] = value;
171
+ } else if (Array.isArray(existing)) {
172
+ existing.push(value);
173
+ } else {
174
+ this._headers[key] = [existing, value];
175
+ }
176
+ this._headerNames[key] = name;
177
+ }
178
+ getHeader(name) { return this._headers[name.toLowerCase()]; }
179
+ getHeaders() { return { ...this._headers }; }
180
+ getHeaderNames() { return Object.keys(this._headers); }
181
+ hasHeader(name) { return name.toLowerCase() in this._headers; }
182
+ removeHeader(name) { delete this._headers[name.toLowerCase()]; delete this._headerNames[name.toLowerCase()]; }
183
+ writeHead(code, msg, hdrs) {
184
+ this.statusCode = code;
185
+ if (typeof msg === "string") this.statusMessage = msg;
186
+ else if (typeof msg === "object") hdrs = msg;
187
+ if (hdrs) {
188
+ for (const [k, v] of Object.entries(hdrs)) {
189
+ // Array values (e.g., Set-Cookie) should be stored as-is
190
+ if (Array.isArray(v)) {
191
+ this._headers[k.toLowerCase()] = v;
192
+ this._headerNames[k.toLowerCase()] = k;
193
+ } else {
194
+ this.setHeader(k, v);
195
+ }
196
+ }
197
+ }
198
+ this.headersSent = true;
199
+ return this;
200
+ }
201
+ write(chunk, encoding, cb) {
202
+ if (typeof encoding === "function") { cb = encoding; encoding = undefined; }
203
+ this.emit("data", chunk);
204
+ if (cb) cb();
205
+ return true;
206
+ }
207
+ end(chunk, encoding, cb) {
208
+ if (typeof chunk === "function") { cb = chunk; chunk = null; }
209
+ if (typeof encoding === "function") { cb = encoding; encoding = null; }
210
+ if (chunk) this.emit("data", chunk);
211
+ this.finished = true;
212
+ this.writable = false;
213
+ this.emit("finish");
214
+ this.emit("close");
215
+ if (cb) cb();
216
+ return this;
217
+ }
218
+ get writableEnded() { return this.finished; }
219
+ get writableFinished() { return this.finished; }
220
+ flushHeaders() { this.headersSent = true; }
221
+ assignSocket() {}
222
+ detachSocket() {}
223
+ writeContinue() {}
224
+ writeProcessing() {}
225
+ setTimeout() { return this; }
226
+ addTrailers() {}
227
+ cork() {}
228
+ uncork() {}
229
+ // Writable interface stubs
230
+ destroy() { return this; }
231
+ }
232
+
233
+ export function createServer() { throw new Error("http.createServer not available in CF Workers"); }
234
+ export function request() { throw new Error("http.request not available in CF Workers"); }
235
+ export function get() { throw new Error("http.get not available in CF Workers"); }
236
+
237
+ export const METHODS = ["GET","POST","PUT","DELETE","PATCH","HEAD","OPTIONS"];
238
+ export const STATUS_CODES = { 200:"OK",201:"Created",204:"No Content",301:"Moved Permanently",302:"Found",304:"Not Modified",400:"Bad Request",401:"Unauthorized",403:"Forbidden",404:"Not Found",500:"Internal Server Error" };
239
+
240
+ export default { IncomingMessage, ServerResponse, createServer, request, get, METHODS, STATUS_CODES };
@@ -0,0 +1,18 @@
1
+ // Image optimization shim for CF Workers.
2
+ // Delegates to Cloudflare Image Resizing instead of sharp.
3
+ // See: https://developers.cloudflare.com/images/transform-images/
4
+
5
+ export async function optimizeImage(buffer, { width, height, quality, contentType }) {
6
+ // CF Image Resizing is handled at the edge via cf.image options
7
+ // on fetch requests. For the adapter, we return the original image
8
+ // since optimization happens at the CDN layer, not in the worker.
9
+ //
10
+ // In production, the dispatch worker or CDN rules apply:
11
+ // fetch(imageUrl, { cf: { image: { width, height, quality, format } } })
12
+ return {
13
+ buffer,
14
+ contentType: contentType || "image/webp",
15
+ };
16
+ }
17
+
18
+ export default { optimizeImage };
@@ -0,0 +1,123 @@
1
+ // Shim for next/dist/server/load-manifest.external.js
2
+ // Reads manifests from globalThis.__MANIFESTS (embedded at build time by the adapter).
3
+
4
+ const cache = new Map();
5
+
6
+ // Manifests that Next.js loads with `handleMissing: true` or that are
7
+ // conditionally generated and may not exist in every build. Returning {} for
8
+ // these matches the upstream Node fs behavior — without this, dynamic routes
9
+ // 500 when the build-time glob scan didn't pick them up.
10
+ //
11
+ // Source: next/dist/server/route-modules/route-module.ts
12
+ // Aligned with opennextjs-cloudflare#1151 + #1160.
13
+ const KNOWN_OPTIONAL_MANIFESTS = new Set([
14
+ "react-loadable-manifest", // Turbopack: only routes with dynamic imports
15
+ "subresource-integrity-manifest", // only when experimental.sri configured
16
+ "server-reference-manifest", // App Router only
17
+ "dynamic-css-manifest", // Pages Router + Webpack only
18
+ "fallback-build-manifest", // only for /_error
19
+ "prefetch-hints", // Next 16.2+
20
+ "_client-reference-manifest", // optional for static metadata routes (evalManifest)
21
+ ]);
22
+
23
+ function isKnownOptional(manifestPath) {
24
+ // Compare against the basename without trailing extension. Some Next.js
25
+ // constants omit the extension (e.g. SUBRESOURCE_INTEGRITY_MANIFEST), so we
26
+ // strip .json / .js before matching.
27
+ const base = manifestPath.split("/").pop() || manifestPath;
28
+ const stripped = base.replace(/\.(json|js)$/, "");
29
+ return KNOWN_OPTIONAL_MANIFESTS.has(stripped);
30
+ }
31
+
32
+ function findInManifests(path) {
33
+ const manifests = globalThis.__MANIFESTS;
34
+ if (!manifests) return undefined;
35
+
36
+ // Direct match
37
+ if (manifests[path]) return manifests[path];
38
+
39
+ // Match by .next/ relative tail — handles path prefix differences
40
+ // Requested: //.next/routes-manifest.json
41
+ // Available: /Users/.../apps/www/.next/routes-manifest.json
42
+ const tail = path.includes(".next/")
43
+ ? ".next/" + path.split(".next/").pop()
44
+ : path.split("/").pop();
45
+
46
+ for (const [key, val] of Object.entries(manifests)) {
47
+ const keyTail = key.includes(".next/")
48
+ ? ".next/" + key.split(".next/").pop()
49
+ : key.split("/").pop();
50
+ if (tail === keyTail) return val;
51
+ }
52
+
53
+ return undefined;
54
+ }
55
+
56
+ function loadManifest(path, shouldCache = true, _cache = cache, skipParse = false, handleMissing) {
57
+ const cached = shouldCache && cache.get(path);
58
+ if (cached) return cached;
59
+
60
+ const content = findInManifests(path);
61
+
62
+ if (content === undefined) {
63
+ if (handleMissing || isKnownOptional(path)) {
64
+ const result = {};
65
+ if (shouldCache) cache.set(path, result);
66
+ return result;
67
+ }
68
+ throw new Error(`[Creek] Manifest not found: ${path}`);
69
+ }
70
+
71
+ let manifest = skipParse ? content : JSON.parse(content);
72
+ if (shouldCache) cache.set(path, manifest);
73
+ return manifest;
74
+ }
75
+
76
+ function evalManifest(path, shouldCache = true, _cache = cache, handleMissing) {
77
+ const cached = shouldCache && cache.get(path);
78
+ if (cached) return cached;
79
+
80
+ const content = findInManifests(path);
81
+
82
+ if (content === undefined) {
83
+ if (handleMissing || isKnownOptional(path)) {
84
+ const result = {};
85
+ if (shouldCache) cache.set(path, result);
86
+ return result;
87
+ }
88
+ throw new Error(`[Creek] Manifest not found for eval: ${path}`);
89
+ }
90
+
91
+ let contextObject = {};
92
+ try {
93
+ contextObject = JSON.parse(content);
94
+ } catch {
95
+ // JS manifests (e.g., _buildManifest.js) — extract JSON via regex.
96
+ // CF Workers blocks new Function() (CSP), so we parse the assignment
97
+ // pattern: self.__BUILD_MANIFEST = {...}
98
+ try {
99
+ const jsonMatch = content.match(/=\s*(\{[\s\S]*\})\s*[;\n]/);
100
+ if (jsonMatch) {
101
+ const data = JSON.parse(jsonMatch[1]);
102
+ for (const [key, val] of Object.entries(data)) {
103
+ contextObject[key] = val;
104
+ }
105
+ }
106
+ } catch {}
107
+ }
108
+
109
+ if (shouldCache) cache.set(path, contextObject);
110
+ return contextObject;
111
+ }
112
+
113
+ function loadManifestFromRelativePath({ projectDir, distDir, manifest, shouldCache, cache: c, skipParse, handleMissing, useEval }) {
114
+ const manifestPath = (projectDir || "") + "/" + (distDir || ".next") + "/" + manifest;
115
+ if (useEval) return evalManifest(manifestPath, shouldCache, c, handleMissing);
116
+ return loadManifest(manifestPath, shouldCache, c, skipParse, handleMissing);
117
+ }
118
+
119
+ function clearManifestCache(path) {
120
+ return cache.delete(path);
121
+ }
122
+
123
+ module.exports = { loadManifest, evalManifest, loadManifestFromRelativePath, clearManifestCache };
@@ -0,0 +1,229 @@
1
+ // Minimal @opentelemetry/api shim for CF Workers
2
+ // Next.js uses it for tracing but it's not required for functionality.
3
+
4
+ const NOOP = () => {};
5
+ // NOOP_SPAN must be usable by Next.js (has methods like isRecording/end)
6
+ // but must NOT be serializable by React RSC. React only serializes
7
+ // enumerable own properties — so we use non-enumerable properties.
8
+ const NOOP_SPAN = Object.create(null);
9
+ Object.defineProperties(NOOP_SPAN, {
10
+ setAttribute: { value: () => NOOP_SPAN },
11
+ setAttributes: { value: () => NOOP_SPAN },
12
+ addEvent: { value: () => NOOP_SPAN },
13
+ setStatus: { value: () => NOOP_SPAN },
14
+ updateName: { value: () => NOOP_SPAN },
15
+ end: { value: NOOP },
16
+ isRecording: { value: () => false },
17
+ recordException: { value: NOOP },
18
+ spanContext: { value: () => ({ traceId: "", spanId: "", traceFlags: 0 }) },
19
+ });
20
+ const NOOP_TRACER = {
21
+ startSpan: () => NOOP_SPAN,
22
+ startActiveSpan: (name, ...args) => {
23
+ const fn = args[args.length - 1];
24
+ if (typeof fn === "function") return fn(NOOP_SPAN);
25
+ return NOOP_SPAN;
26
+ },
27
+ };
28
+ const NOOP_TRACER_PROVIDER = { getTracer: () => NOOP_TRACER };
29
+ const NOOP_CONTEXT_MANAGER = {
30
+ active: () => ROOT_CONTEXT,
31
+ with: (ctx, fn) => fn(),
32
+ bind: (ctx, target) => target,
33
+ enable: NOOP, disable: NOOP,
34
+ };
35
+
36
+ // Context objects need getValue/setValue/deleteValue per OpenTelemetry API.
37
+ // setValue + deleteValue must return a NEW context with the key's value
38
+ // set/removed — not mutate in place, and not return the same empty ROOT_CONTEXT
39
+ // (or the tracer code calling \`trace.setSpan(ctx, span)\` would get a
40
+ // context that still doesn't contain the span).
41
+ function _makeContext(store) {
42
+ return {
43
+ getValue: (key) => store.get(key),
44
+ setValue: (key, value) => {
45
+ const next = new Map(store);
46
+ next.set(key, value);
47
+ return _makeContext(next);
48
+ },
49
+ deleteValue: (key) => {
50
+ const next = new Map(store);
51
+ next.delete(key);
52
+ return _makeContext(next);
53
+ },
54
+ };
55
+ }
56
+ export const ROOT_CONTEXT = _makeContext(new Map());
57
+ export const defaultTextMapGetter = { get: () => undefined, keys: () => [] };
58
+ export const defaultTextMapSetter = { set: NOOP };
59
+ export const INVALID_SPANID = "";
60
+ export const INVALID_TRACEID = "";
61
+ export const INVALID_SPAN_CONTEXT = { traceId: "", spanId: "", traceFlags: 0 };
62
+
63
+ // \`trace\` and \`context\` also have to cooperate across @opentelemetry/api
64
+ // copies via the same globalThis symbol that \`propagation\` uses below.
65
+ // Next.js's tracer.js imports the trace API through our shim; when the
66
+ // user's instrumentation runs \`NodeTracerProvider.register()\`, it writes
67
+ // the real tracer provider and context manager into
68
+ // \`globalThis[Symbol.for("opentelemetry.js.api.1")]\` via its own api
69
+ // copy. Without delegating here, Next.js's render path keeps calling
70
+ // NOOP_TRACER.startSpan + context.active()=ROOT_CONTEXT, so no real span
71
+ // is ever present in the context passed to \`propagation.inject\`. Result:
72
+ // the user's propagator sees no span and falls back to
73
+ // \`"invariant"\` for \`my-parent-span-id\`, failing the regex assertion
74
+ // \`/<meta name="my-parent-span-id" content="[a-f0-9]{16}">/\`. Reading
75
+ // from the shared global recovers the real tracer + context manager and
76
+ // gives the propagator a real span to read.
77
+ const CONTEXT_API_KEY = Symbol.for("opentelemetry.context");
78
+ const SPAN_KEY = Symbol.for("OpenTelemetry Context Key SPAN");
79
+ function _getGlobalTracerProvider() {
80
+ return _otelGlobal().trace ?? null;
81
+ }
82
+ function _getGlobalContextManager() {
83
+ return _otelGlobal().context ?? null;
84
+ }
85
+ export const trace = {
86
+ getTracer: (...args) => {
87
+ const g = _getGlobalTracerProvider();
88
+ if (g && typeof g.getTracer === "function") {
89
+ try { return g.getTracer(...args); } catch {}
90
+ }
91
+ return NOOP_TRACER;
92
+ },
93
+ getTracerProvider: () => _getGlobalTracerProvider() ?? NOOP_TRACER_PROVIDER,
94
+ setGlobalTracerProvider: (p) => { _otelGlobal().trace = p; return p; },
95
+ // getSpan(ctx): read the SPAN key from the context object — the same
96
+ // pattern sdk-trace-base uses. Falls back to undefined when the context
97
+ // is our empty ROOT_CONTEXT.
98
+ getSpan: (ctx) => {
99
+ if (ctx && typeof ctx.getValue === "function") {
100
+ const s = ctx.getValue(SPAN_KEY);
101
+ if (s) return s;
102
+ }
103
+ return undefined;
104
+ },
105
+ getActiveSpan: () => {
106
+ const ctx = _callActiveContext();
107
+ if (ctx && typeof ctx.getValue === "function") return ctx.getValue(SPAN_KEY);
108
+ return undefined;
109
+ },
110
+ setSpan: (ctx, span) => (ctx || ROOT_CONTEXT).setValue(SPAN_KEY, span),
111
+ deleteSpan: (ctx) => (ctx || ROOT_CONTEXT).deleteValue(SPAN_KEY),
112
+ setSpanContext: (ctx) => ctx || ROOT_CONTEXT,
113
+ // getSpanContext: read span from context, return its spanContext() result.
114
+ // User propagators rely on \`trace.getSpanContext(ctx)?.spanId\` to fill
115
+ // their my-parent-span-id meta tag — without this, they fall back to
116
+ // the literal string "invariant".
117
+ getSpanContext: (ctx) => {
118
+ const span = trace.getSpan(ctx);
119
+ if (span && typeof span.spanContext === "function") {
120
+ try { return span.spanContext(); } catch {}
121
+ }
122
+ return undefined;
123
+ },
124
+ isSpanContextValid: (sc) => !!sc && typeof sc.traceId === "string" && sc.traceId.length > 0,
125
+ };
126
+
127
+ function _callActiveContext() {
128
+ const mgr = _getGlobalContextManager();
129
+ if (mgr && typeof mgr.active === "function") {
130
+ try { return mgr.active(); } catch {}
131
+ }
132
+ return ROOT_CONTEXT;
133
+ }
134
+ export const context = {
135
+ active: () => _callActiveContext(),
136
+ with: (ctx, fn, thisArg, ...args) => {
137
+ const mgr = _getGlobalContextManager();
138
+ if (mgr && typeof mgr.with === "function") {
139
+ try { return mgr.with(ctx, fn, thisArg, ...args); } catch {}
140
+ }
141
+ return fn.call(thisArg, ...args);
142
+ },
143
+ bind: (ctx, target) => {
144
+ const mgr = _getGlobalContextManager();
145
+ if (mgr && typeof mgr.bind === "function") {
146
+ try { return mgr.bind(ctx, target); } catch {}
147
+ }
148
+ return target;
149
+ },
150
+ setGlobalContextManager: (mgr) => { _otelGlobal().context = mgr; return mgr; },
151
+ disable: () => { delete _otelGlobal().context; },
152
+ };
153
+
154
+ // Propagation must be functional (not NOOP) for user instrumentation to
155
+ // work — Next.js's server render calls \`propagation.inject(ctx, carrier,
156
+ // setter)\` when \`experimental.clientTraceMetadata\` is configured, and
157
+ // expects the user's propagator (set by \`NodeTracerProvider.register({
158
+ // propagator })\`) to emit the \`<meta name="...">\` tags.
159
+ //
160
+ // Delicate: \`@opentelemetry/api\` is loaded as multiple copies in the
161
+ // bundle — one compiled into \`next/\`, one Turbopack-bundled from the
162
+ // user's \`node_modules\`, and this shim (aliased for bare-specifier
163
+ // imports at wrangler time). Their code agrees on a shared singleton by
164
+ // writing propagator/context/tracer onto
165
+ // \`globalThis[Symbol.for("opentelemetry.js.api.<major>")].propagation\`.
166
+ // If this shim's propagation used a module-local variable, the user's
167
+ // \`setGlobalPropagator\` call on their bundled api would never reach
168
+ // this shim's \`inject\` — which is the codepath Next.js actually calls
169
+ // through. Writing/reading via the same global symbol keeps every copy
170
+ // in sync.
171
+ // Fixes e2e/opentelemetry/client-trace-metadata (5 tests).
172
+ const OTEL_GLOBAL_KEY = Symbol.for("opentelemetry.js.api.1");
173
+ function _otelGlobal() {
174
+ let g = globalThis[OTEL_GLOBAL_KEY];
175
+ if (!g) {
176
+ g = { version: "1.9.0" };
177
+ globalThis[OTEL_GLOBAL_KEY] = g;
178
+ }
179
+ return g;
180
+ }
181
+ function _getPropagator() {
182
+ return _otelGlobal().propagation ?? null;
183
+ }
184
+ export const propagation = {
185
+ inject: (ctx, carrier, setter) => {
186
+ const p = _getPropagator();
187
+ if (p && typeof p.inject === "function") {
188
+ try { p.inject(ctx, carrier, setter); } catch {}
189
+ }
190
+ },
191
+ extract: (ctx, carrier, getter) => {
192
+ const p = _getPropagator();
193
+ if (p && typeof p.extract === "function") {
194
+ try { return p.extract(ctx, carrier, getter); } catch {}
195
+ }
196
+ return ctx;
197
+ },
198
+ fields: () => {
199
+ const p = _getPropagator();
200
+ if (p && typeof p.fields === "function") {
201
+ try { return p.fields(); } catch {}
202
+ }
203
+ return [];
204
+ },
205
+ setGlobalPropagator: (p) => {
206
+ _otelGlobal().propagation = p;
207
+ return propagation;
208
+ },
209
+ disable: () => { delete _otelGlobal().propagation; },
210
+ };
211
+
212
+ export const diag = {
213
+ setLogger: NOOP, disable: NOOP,
214
+ createComponentLogger: () => diag,
215
+ verbose: NOOP, debug: NOOP, info: NOOP, warn: NOOP, error: NOOP,
216
+ };
217
+
218
+ export function createContextKey(name) { return Symbol(name); }
219
+
220
+ export const SpanKind = { INTERNAL: 0, SERVER: 1, CLIENT: 2, PRODUCER: 3, CONSUMER: 4 };
221
+ export const SpanStatusCode = { UNSET: 0, OK: 1, ERROR: 2 };
222
+ export const TraceFlags = { NONE: 0, SAMPLED: 1 };
223
+
224
+ export default {
225
+ trace, context, propagation, diag, createContextKey,
226
+ ROOT_CONTEXT, SpanKind, SpanStatusCode, TraceFlags,
227
+ defaultTextMapGetter, defaultTextMapSetter,
228
+ INVALID_SPANID, INVALID_TRACEID, INVALID_SPAN_CONTEXT,
229
+ };
@@ -0,0 +1,12 @@
1
+ // sharp shim for Cloudflare Workers.
2
+ //
3
+ // `@vercel/og/index.node.js` tries `(await import("sharp")).default` and,
4
+ // if truthy, uses sharp to rasterize satori's SVG output to PNG. sharp
5
+ // relies on native `.node` bindings (libvips) that workerd cannot load,
6
+ // so the real module half-loads and ends up non-callable — the caller
7
+ // then throws `TypeError: sharp is not a function`.
8
+ //
9
+ // Returning `default: undefined` makes getSharp() return undefined and
10
+ // @vercel/og falls back to its built-in resvg.wasm path, which is already
11
+ // bundled as a CompiledWasm module and works natively on workerd.
12
+ export default undefined;