@visiolab/effect-firebase-functions 0.0.0-stage → 0.3.1

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,79 @@
1
+ import { layer } from "../logger/CloudLogging.mjs";
2
+ import { DeferredTracer, externalParent } from "./serverSpan.mjs";
3
+ import { Context, Effect, Exit, Layer, Logger, Scope } from "effect";
4
+ // src/internal/runtime.ts
5
+ /**
6
+ * What every kind of Function runs an event or a call under: the lazy,
7
+ * memoized build of its Layer, its server span, its Tracer and its logs.
8
+ *
9
+ * A kind's runtime turns what `firebase-functions` hands over into the input
10
+ * of its core `serve`, and passes that to {@link make}'s run function, which
11
+ * does the rest.
12
+ *
13
+ * @internal
14
+ */
15
+ /**
16
+ * The memo map and scope every Layer of this process is built in, so a Layer
17
+ * that several exports share is built once. The scope is never closed:
18
+ * functions-framework exits the process without waiting for finalizers.
19
+ */
20
+ const memoMap = Layer.makeMemoMapUnsafe();
21
+ const scope = Scope.makeUnsafe();
22
+ /**
23
+ * Builds `live` on the first event, and hands every later event the same
24
+ * services. Concurrent first events share one build. The build runs under the
25
+ * span and Tracer of the event that starts it, so it nests under that event.
26
+ *
27
+ * A failed build fails the event and is tried again on the next one. A Layer
28
+ * that fails closes the scope it was built in, which drops its failed entries
29
+ * from the memo map, so the retry builds anew.
30
+ */
31
+ const lazyContext = (live) => {
32
+ let pending;
33
+ return Effect.flatMap(Effect.context(), (caller) => {
34
+ pending ??= Effect.runPromiseExit(Layer.buildWithMemoMap(live, memoMap, scope).pipe(Effect.provideContext(caller))).then((exit) => {
35
+ if (Exit.isFailure(exit)) pending = void 0;
36
+ return exit;
37
+ });
38
+ const attempt = pending;
39
+ return Effect.flatten(Effect.promise(() => attempt));
40
+ });
41
+ };
42
+ /**
43
+ * The run function of one export: it builds `live` on the export's first
44
+ * event or call, so a worker that serves one export builds nothing for the
45
+ * others, and runs each `body` with the services of `live` provided.
46
+ *
47
+ * The run happens in a `kind: "server"` span, parented on the trace context
48
+ * Firebase read from the event or request. Its spans go to the Tracer of
49
+ * `live`, its logs go through `CloudLogging.layer` unless `live` provides a
50
+ * logger, and logs from before `live` is built wait until the spans have
51
+ * their final ids.
52
+ *
53
+ * Read the trace context synchronously, at the start of the SDK's handler:
54
+ * call the run function there.
55
+ *
56
+ * @internal
57
+ */
58
+ const make = (live, options) => {
59
+ const context = lazyContext(live);
60
+ const spanOptions = {
61
+ kind: "server",
62
+ attributes: options.attributes
63
+ };
64
+ return (body, signal) => {
65
+ const parent = externalParent();
66
+ const deferred = new DeferredTracer();
67
+ return Effect.runPromiseExit(Effect.gen(function* () {
68
+ const services = yield* context.pipe(Effect.onError(() => Effect.sync(() => deferred.attach(Context.empty()))), Effect.onError(options.onBuildFailure));
69
+ deferred.attach(services);
70
+ return yield* Effect.provideContext(body, services);
71
+ }).pipe(Effect.withSpan(options.span, {
72
+ ...spanOptions,
73
+ parent
74
+ }), Effect.updateService(Logger.CurrentLoggers, (loggers) => deferred.holdLogs(loggers)), Effect.provide(layer), Effect.withTracer(deferred.tracer)), signal === void 0 ? void 0 : { signal });
75
+ };
76
+ };
77
+ export { make };
78
+
79
+ //# sourceMappingURL=runtime.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime.mjs","names":["CloudLogging.layer"],"sources":["../../src/internal/runtime.ts"],"sourcesContent":["/**\n * What every kind of Function runs an event or a call under: the lazy,\n * memoized build of its Layer, its server span, its Tracer and its logs.\n *\n * A kind's runtime turns what `firebase-functions` hands over into the input\n * of its core `serve`, and passes that to {@link make}'s run function, which\n * does the rest.\n *\n * @internal\n */\nimport { type Cause, Context, Effect, Exit, Layer, Logger, Scope } from \"effect\";\nimport * as CloudLogging from \"../logger/CloudLogging.ts\";\nimport { DeferredTracer, externalParent } from \"./serverSpan.ts\";\n\n/**\n * The memo map and scope every Layer of this process is built in, so a Layer\n * that several exports share is built once. The scope is never closed:\n * functions-framework exits the process without waiting for finalizers.\n */\nconst memoMap = Layer.makeMemoMapUnsafe();\nconst scope = Scope.makeUnsafe();\n\n/**\n * Builds `live` on the first event, and hands every later event the same\n * services. Concurrent first events share one build. The build runs under the\n * span and Tracer of the event that starts it, so it nests under that event.\n *\n * A failed build fails the event and is tried again on the next one. A Layer\n * that fails closes the scope it was built in, which drops its failed entries\n * from the memo map, so the retry builds anew.\n */\nconst lazyContext = <R, E>(live: Layer.Layer<R, E>) => {\n let pending: Promise<Exit.Exit<Context.Context<R>, E>> | undefined;\n return Effect.flatMap(Effect.context<never>(), (caller) => {\n pending ??= Effect.runPromiseExit(\n Layer.buildWithMemoMap(live, memoMap, scope).pipe(Effect.provideContext(caller)),\n ).then((exit) => {\n if (Exit.isFailure(exit)) pending = undefined;\n return exit;\n });\n const attempt = pending;\n return Effect.flatten(Effect.promise(() => attempt));\n });\n};\n\n/**\n * How one export runs each event or call.\n *\n * @internal\n */\nexport interface Options {\n /** The name of the `kind: \"server\"` span each event or call runs in. */\n readonly span: string;\n /** The attributes of that span. */\n readonly attributes: Readonly<Record<string, unknown>>;\n /**\n * Logs the Cause of a Layer that failed to build, in the span of the event\n * or call that built it.\n */\n readonly onBuildFailure: (cause: Cause.Cause<unknown>) => Effect.Effect<void>;\n}\n\n/**\n * The run function of one export: it builds `live` on the export's first\n * event or call, so a worker that serves one export builds nothing for the\n * others, and runs each `body` with the services of `live` provided.\n *\n * The run happens in a `kind: \"server\"` span, parented on the trace context\n * Firebase read from the event or request. Its spans go to the Tracer of\n * `live`, its logs go through `CloudLogging.layer` unless `live` provides a\n * logger, and logs from before `live` is built wait until the spans have\n * their final ids.\n *\n * Read the trace context synchronously, at the start of the SDK's handler:\n * call the run function there.\n *\n * @internal\n */\nexport const make = <R, E>(live: Layer.Layer<R, E>, options: Options) => {\n const context = lazyContext(live);\n const spanOptions = { kind: \"server\", attributes: options.attributes } as const;\n return <A>(\n body: Effect.Effect<A, unknown, unknown>,\n signal?: AbortSignal,\n ): Promise<Exit.Exit<A, unknown>> => {\n const parent = externalParent();\n const deferred = new DeferredTracer();\n return Effect.runPromiseExit(\n Effect.gen(function* () {\n const services = yield* context.pipe(\n // With no services to take a Tracer from, the run's spans and held\n // logs go to the native Tracer, so the failure is logged in one trace.\n Effect.onError(() => Effect.sync(() => deferred.attach(Context.empty()))),\n Effect.onError(options.onBuildFailure),\n );\n deferred.attach(services as Context.Context<never>);\n // The public signatures check that `live` provides what `body` needs.\n return yield* Effect.provideContext(body, services as Context.Context<any>);\n }).pipe(\n Effect.withSpan(options.span, { ...spanOptions, parent }),\n // Logs from before `live` is built wait for the spans' real ids.\n Effect.updateService(Logger.CurrentLoggers, (loggers) => deferred.holdLogs(loggers)),\n // Beneath `live`: a logger that `live` provides replaces this one.\n Effect.provide(CloudLogging.layer),\n Effect.withTracer(deferred.tracer),\n ),\n signal === undefined ? undefined : { signal },\n );\n };\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAmBA,MAAM,UAAU,MAAM,kBAAkB;AACxC,MAAM,QAAQ,MAAM,WAAW;;;;;;;;;;AAW/B,MAAM,eAAqB,SAA4B;CACrD,IAAI;CACJ,OAAO,OAAO,QAAQ,OAAO,QAAe,IAAI,WAAW;EACzD,YAAY,OAAO,eACjB,MAAM,iBAAiB,MAAM,SAAS,KAAK,CAAC,CAAC,KAAK,OAAO,eAAe,MAAM,CAAC,CACjF,CAAC,CAAC,MAAM,SAAS;GACf,IAAI,KAAK,UAAU,IAAI,GAAG,UAAU,KAAA;GACpC,OAAO;EACT,CAAC;EACD,MAAM,UAAU;EAChB,OAAO,OAAO,QAAQ,OAAO,cAAc,OAAO,CAAC;CACrD,CAAC;AACH;;;;;;;;;;;;;;;;;AAmCA,MAAa,QAAc,MAAyB,YAAqB;CACvE,MAAM,UAAU,YAAY,IAAI;CAChC,MAAM,cAAc;EAAE,MAAM;EAAU,YAAY,QAAQ;CAAW;CACrE,QACE,MACA,WACmC;EACnC,MAAM,SAAS,eAAe;EAC9B,MAAM,WAAW,IAAI,eAAe;EACpC,OAAO,OAAO,eACZ,OAAO,IAAI,aAAa;GACtB,MAAM,WAAW,OAAO,QAAQ,KAG9B,OAAO,cAAc,OAAO,WAAW,SAAS,OAAO,QAAQ,MAAM,CAAC,CAAC,CAAC,GACxE,OAAO,QAAQ,QAAQ,cAAc,CACvC;GACA,SAAS,OAAO,QAAkC;GAElD,OAAO,OAAO,OAAO,eAAe,MAAM,QAAgC;EAC5E,CAAC,CAAC,CAAC,KACD,OAAO,SAAS,QAAQ,MAAM;GAAE,GAAG;GAAa;EAAO,CAAC,GAExD,OAAO,cAAc,OAAO,iBAAiB,YAAY,SAAS,SAAS,OAAO,CAAC,GAEnF,OAAO,QAAQA,KAAkB,GACjC,OAAO,WAAW,SAAS,MAAM,CACnC,GACA,WAAW,KAAA,IAAY,KAAA,IAAY,EAAE,OAAO,CAC9C;CACF;AACF"}
@@ -0,0 +1,187 @@
1
+ import { Context, Logger, Option, Tracer } from "effect";
2
+ import { traceContext } from "firebase-functions/v2";
3
+ // src/internal/serverSpan.ts
4
+ /** @internal */
5
+ /**
6
+ * The parent of a call's server span: the trace context `firebase-functions`
7
+ * read from the request's headers, the `X-Cloud-Trace-Context` header winning
8
+ * over `traceparent`. Read it synchronously at the start of the call, before
9
+ * any fiber runs: Effect's scheduler may later run under another request's
10
+ * async store. `undefined` when the request carried none, as in the emulator.
11
+ *
12
+ * @internal
13
+ */
14
+ const externalParent = () => {
15
+ const store = traceContext.getStore();
16
+ if (store === void 0) return void 0;
17
+ return Tracer.externalSpan({
18
+ traceId: store.traceId,
19
+ spanId: store.parentId.padStart(16, "0"),
20
+ sampled: store.sample
21
+ });
22
+ };
23
+ /**
24
+ * A span opened before the Tracer of `Live` is known. It records what happens
25
+ * to it, and replays that onto the real span once {@link DeferredTracer.attach}
26
+ * creates it. From then on it forwards everything.
27
+ */
28
+ var DeferredSpan = class DeferredSpan {
29
+ _tag = "Span";
30
+ name;
31
+ parent;
32
+ annotations;
33
+ kind;
34
+ sampled;
35
+ options;
36
+ real;
37
+ calls = [];
38
+ pendingAttributes = /* @__PURE__ */ new Map();
39
+ pendingLinks;
40
+ pendingStatus;
41
+ provisional;
42
+ constructor(options) {
43
+ this.options = options;
44
+ this.name = options.name;
45
+ this.parent = options.parent;
46
+ this.annotations = options.annotations;
47
+ this.kind = options.kind;
48
+ this.sampled = options.sampled;
49
+ this.pendingLinks = [...options.links];
50
+ this.pendingStatus = {
51
+ _tag: "Started",
52
+ startTime: options.startTime
53
+ };
54
+ }
55
+ get ids() {
56
+ if (this.provisional === void 0) {
57
+ const native = new Tracer.NativeSpan({
58
+ ...this.options,
59
+ links: []
60
+ });
61
+ this.provisional = {
62
+ traceId: native.traceId,
63
+ spanId: native.spanId
64
+ };
65
+ }
66
+ return this.provisional;
67
+ }
68
+ get traceId() {
69
+ return this.real?.traceId ?? this.ids.traceId;
70
+ }
71
+ get spanId() {
72
+ return this.real?.spanId ?? this.ids.spanId;
73
+ }
74
+ get status() {
75
+ return this.real?.status ?? this.pendingStatus;
76
+ }
77
+ get attributes() {
78
+ return this.real?.attributes ?? this.pendingAttributes;
79
+ }
80
+ get links() {
81
+ return this.real?.links ?? this.pendingLinks;
82
+ }
83
+ end(endTime, exit) {
84
+ if (this.real !== void 0) return this.real.end(endTime, exit);
85
+ if (this.pendingStatus._tag === "Ended") return;
86
+ this.pendingStatus = {
87
+ _tag: "Ended",
88
+ startTime: this.pendingStatus.startTime,
89
+ endTime,
90
+ exit
91
+ };
92
+ }
93
+ attribute(key, value) {
94
+ if (this.real !== void 0) return this.real.attribute(key, value);
95
+ this.pendingAttributes.set(key, value);
96
+ }
97
+ event(name, startTime, attributes) {
98
+ if (this.real !== void 0) return this.real.event(name, startTime, attributes);
99
+ this.calls.push((span) => span.event(name, startTime, attributes));
100
+ }
101
+ addLinks(links) {
102
+ if (this.real !== void 0) return this.real.addLinks(links);
103
+ this.calls.push((span) => span.addLinks(links));
104
+ }
105
+ /** Opens the real span on `tracer` and replays onto it what happened so far. */
106
+ attach(tracer) {
107
+ const parent = Option.map(this.parent, (parent) => parent instanceof DeferredSpan ? parent.real ?? parent : parent);
108
+ const real = tracer.span({
109
+ ...this.options,
110
+ parent,
111
+ links: this.pendingLinks
112
+ });
113
+ for (const [key, value] of this.pendingAttributes) real.attribute(key, value);
114
+ for (const call of this.calls) call(real);
115
+ if (this.pendingStatus._tag === "Ended") real.end(this.pendingStatus.endTime, this.pendingStatus.exit);
116
+ this.real = real;
117
+ }
118
+ };
119
+ /**
120
+ * The Tracer a call starts under. A call's server span opens before `Live` is
121
+ * built, and so before its Tracer is known, so that a first call's Layer
122
+ * build nests under it. This Tracer defers the spans opened until then, and
123
+ * {@link DeferredTracer.attach} hands them to the Tracer of `Live`, which
124
+ * exports them with their original times and parents.
125
+ *
126
+ * @internal
127
+ */
128
+ var DeferredTracer = class {
129
+ deferred = [];
130
+ held = [];
131
+ real;
132
+ /**
133
+ * Wraps each of `loggers` so that an entry logged before {@link attach}
134
+ * is held, and logged on `attach`, once the spans it was logged under have
135
+ * their real ids. A logger reads the trace of the current span, and a
136
+ * deferred span's ids before `attach` are ones no exporter sees.
137
+ *
138
+ * The tracer logger isn't held: it writes onto the span itself, which a
139
+ * deferred span already replays.
140
+ */
141
+ holdLogs(loggers) {
142
+ return new Set(Array.from(loggers, (logger) => logger === Logger.tracerLogger ? logger : Logger.make((options) => {
143
+ if (this.real !== void 0) return logger.log(options);
144
+ const held = {
145
+ ...options,
146
+ fiber: snapshot(options.fiber)
147
+ };
148
+ this.held.push(() => logger.log(held));
149
+ })));
150
+ }
151
+ tracer = Tracer.make({ span: (options) => {
152
+ if (this.real !== void 0) return this.real.span(options);
153
+ const span = new DeferredSpan(options);
154
+ this.deferred.push(span);
155
+ return span;
156
+ } });
157
+ /**
158
+ * Hands every span opened so far to the Tracer that `services` provide, the
159
+ * native one when they provide none, in the order they were opened, so that
160
+ * each parent exists before its children. Then logs the entries
161
+ * {@link holdLogs} held, in the order they were logged.
162
+ *
163
+ * Attach with no services when `Live` failed to build, so the spans and
164
+ * logs of the failed call still get ids of one trace.
165
+ */
166
+ attach(services) {
167
+ if (this.real !== void 0) return;
168
+ this.real = Context.get(services, Tracer.Tracer);
169
+ for (const span of this.deferred) span.attach(this.real);
170
+ this.deferred.length = 0;
171
+ for (const log of this.held) log();
172
+ this.held.length = 0;
173
+ }
174
+ };
175
+ /**
176
+ * `fiber` as it is now: what a logger reads from it later, such as the log
177
+ * annotations and the current span, stays what it was when the entry was
178
+ * logged. Both are derived from the fiber's context, which never changes in
179
+ * place, only gets replaced.
180
+ */
181
+ const snapshot = (fiber) => Object.create(fiber, {
182
+ context: { value: fiber.context },
183
+ cache: { value: fiber.cache }
184
+ });
185
+ export { DeferredTracer, externalParent };
186
+
187
+ //# sourceMappingURL=serverSpan.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"serverSpan.mjs","names":[],"sources":["../../src/internal/serverSpan.ts"],"sourcesContent":["/** @internal */\nimport { Context, Logger, Option, Tracer, type Exit, type Fiber } from \"effect\";\nimport { traceContext } from \"firebase-functions/v2\";\n\n/**\n * The parent of a call's server span: the trace context `firebase-functions`\n * read from the request's headers, the `X-Cloud-Trace-Context` header winning\n * over `traceparent`. Read it synchronously at the start of the call, before\n * any fiber runs: Effect's scheduler may later run under another request's\n * async store. `undefined` when the request carried none, as in the emulator.\n *\n * @internal\n */\nexport const externalParent = (): Tracer.ExternalSpan | undefined => {\n const store = traceContext.getStore();\n if (store === undefined) return undefined;\n return Tracer.externalSpan({\n traceId: store.traceId,\n // The Cloud header's decimal span id reaches us as unpadded hex.\n spanId: store.parentId.padStart(16, \"0\"),\n sampled: store.sample,\n });\n};\n\ntype SpanOptions = Parameters<Tracer.Tracer[\"span\"]>[0];\n\n/**\n * A span opened before the Tracer of `Live` is known. It records what happens\n * to it, and replays that onto the real span once {@link DeferredTracer.attach}\n * creates it. From then on it forwards everything.\n */\nclass DeferredSpan implements Tracer.Span {\n readonly _tag = \"Span\";\n readonly name: string;\n readonly parent: Option.Option<Tracer.AnySpan>;\n readonly annotations: Context.Context<never>;\n readonly kind: Tracer.SpanKind;\n readonly sampled: boolean;\n readonly options: SpanOptions;\n real: Tracer.Span | undefined;\n // What happened before `real` existed, in order.\n private readonly calls: Array<(span: Tracer.Span) => void> = [];\n private readonly pendingAttributes = new Map<string, unknown>();\n private readonly pendingLinks: Array<Tracer.SpanLink>;\n private pendingStatus: Tracer.SpanStatus;\n private provisional: { traceId: string; spanId: string } | undefined;\n\n constructor(options: SpanOptions) {\n this.options = options;\n this.name = options.name;\n this.parent = options.parent;\n this.annotations = options.annotations;\n this.kind = options.kind;\n this.sampled = options.sampled;\n this.pendingLinks = [...options.links];\n this.pendingStatus = { _tag: \"Started\", startTime: options.startTime };\n }\n\n // Read before `attach` only by a Tracer that `Live` provides to its own\n // Layers: such a span joins the trace, but its parent's span id is one no\n // exporter sees.\n private get ids() {\n if (this.provisional === undefined) {\n const native = new Tracer.NativeSpan({ ...this.options, links: [] });\n this.provisional = { traceId: native.traceId, spanId: native.spanId };\n }\n return this.provisional;\n }\n\n get traceId() {\n return this.real?.traceId ?? this.ids.traceId;\n }\n\n get spanId() {\n return this.real?.spanId ?? this.ids.spanId;\n }\n\n get status() {\n return this.real?.status ?? this.pendingStatus;\n }\n\n get attributes() {\n return this.real?.attributes ?? this.pendingAttributes;\n }\n\n get links() {\n return this.real?.links ?? this.pendingLinks;\n }\n\n end(endTime: bigint, exit: Exit.Exit<unknown, unknown>) {\n if (this.real !== undefined) return this.real.end(endTime, exit);\n if (this.pendingStatus._tag === \"Ended\") return;\n this.pendingStatus = { _tag: \"Ended\", startTime: this.pendingStatus.startTime, endTime, exit };\n }\n\n attribute(key: string, value: unknown) {\n if (this.real !== undefined) return this.real.attribute(key, value);\n this.pendingAttributes.set(key, value);\n }\n\n event(name: string, startTime: bigint, attributes?: Record<string, unknown>) {\n if (this.real !== undefined) return this.real.event(name, startTime, attributes);\n this.calls.push((span) => span.event(name, startTime, attributes));\n }\n\n addLinks(links: ReadonlyArray<Tracer.SpanLink>) {\n if (this.real !== undefined) return this.real.addLinks(links);\n this.calls.push((span) => span.addLinks(links));\n }\n\n /** Opens the real span on `tracer` and replays onto it what happened so far. */\n attach(tracer: Tracer.Tracer) {\n const parent = Option.map(this.parent, (parent) =>\n parent instanceof DeferredSpan ? (parent.real ?? parent) : parent,\n );\n const real = tracer.span({ ...this.options, parent, links: this.pendingLinks });\n for (const [key, value] of this.pendingAttributes) real.attribute(key, value);\n for (const call of this.calls) call(real);\n if (this.pendingStatus._tag === \"Ended\") {\n real.end(this.pendingStatus.endTime, this.pendingStatus.exit);\n }\n this.real = real;\n }\n}\n\n/**\n * The Tracer a call starts under. A call's server span opens before `Live` is\n * built, and so before its Tracer is known, so that a first call's Layer\n * build nests under it. This Tracer defers the spans opened until then, and\n * {@link DeferredTracer.attach} hands them to the Tracer of `Live`, which\n * exports them with their original times and parents.\n *\n * @internal\n */\nexport class DeferredTracer {\n private readonly deferred: Array<DeferredSpan> = [];\n private readonly held: Array<() => void> = [];\n private real: Tracer.Tracer | undefined;\n\n /**\n * Wraps each of `loggers` so that an entry logged before {@link attach}\n * is held, and logged on `attach`, once the spans it was logged under have\n * their real ids. A logger reads the trace of the current span, and a\n * deferred span's ids before `attach` are ones no exporter sees.\n *\n * The tracer logger isn't held: it writes onto the span itself, which a\n * deferred span already replays.\n */\n holdLogs(\n loggers: ReadonlySet<Logger.Logger<unknown, any>>,\n ): ReadonlySet<Logger.Logger<unknown, any>> {\n return new Set(\n Array.from(loggers, (logger) =>\n logger === Logger.tracerLogger\n ? logger\n : Logger.make<unknown, unknown>((options) => {\n if (this.real !== undefined) return logger.log(options);\n const held = { ...options, fiber: snapshot(options.fiber) };\n this.held.push(() => logger.log(held));\n }),\n ),\n );\n }\n\n readonly tracer: Tracer.Tracer = Tracer.make({\n span: (options) => {\n if (this.real !== undefined) return this.real.span(options);\n const span = new DeferredSpan(options);\n this.deferred.push(span);\n return span;\n },\n });\n\n /**\n * Hands every span opened so far to the Tracer that `services` provide, the\n * native one when they provide none, in the order they were opened, so that\n * each parent exists before its children. Then logs the entries\n * {@link holdLogs} held, in the order they were logged.\n *\n * Attach with no services when `Live` failed to build, so the spans and\n * logs of the failed call still get ids of one trace.\n */\n attach(services: Context.Context<never>) {\n if (this.real !== undefined) return;\n this.real = Context.get(services, Tracer.Tracer);\n for (const span of this.deferred) span.attach(this.real);\n this.deferred.length = 0;\n for (const log of this.held) log();\n this.held.length = 0;\n }\n}\n\n/**\n * `fiber` as it is now: what a logger reads from it later, such as the log\n * annotations and the current span, stays what it was when the entry was\n * logged. Both are derived from the fiber's context, which never changes in\n * place, only gets replaced.\n */\nconst snapshot = <A, E>(fiber: Fiber.Fiber<A, E>): Fiber.Fiber<A, E> =>\n Object.create(fiber, {\n context: { value: fiber.context },\n cache: { value: fiber.cache },\n }) as Fiber.Fiber<A, E>;\n"],"mappings":";;;;;;;;;;;;;AAaA,MAAa,uBAAwD;CACnE,MAAM,QAAQ,aAAa,SAAS;CACpC,IAAI,UAAU,KAAA,GAAW,OAAO,KAAA;CAChC,OAAO,OAAO,aAAa;EACzB,SAAS,MAAM;EAEf,QAAQ,MAAM,SAAS,SAAS,IAAI,GAAG;EACvC,SAAS,MAAM;CACjB,CAAC;AACH;;;;;;AASA,IAAM,eAAN,MAAM,aAAoC;CACxC,OAAgB;CAChB;CACA;CACA;CACA;CACA;CACA;CACA;CAEA,QAA6D,CAAC;CAC9D,oCAAqC,IAAI,IAAqB;CAC9D;CACA;CACA;CAEA,YAAY,SAAsB;EAChC,KAAK,UAAU;EACf,KAAK,OAAO,QAAQ;EACpB,KAAK,SAAS,QAAQ;EACtB,KAAK,cAAc,QAAQ;EAC3B,KAAK,OAAO,QAAQ;EACpB,KAAK,UAAU,QAAQ;EACvB,KAAK,eAAe,CAAC,GAAG,QAAQ,KAAK;EACrC,KAAK,gBAAgB;GAAE,MAAM;GAAW,WAAW,QAAQ;EAAU;CACvE;CAKA,IAAY,MAAM;EAChB,IAAI,KAAK,gBAAgB,KAAA,GAAW;GAClC,MAAM,SAAS,IAAI,OAAO,WAAW;IAAE,GAAG,KAAK;IAAS,OAAO,CAAC;GAAE,CAAC;GACnE,KAAK,cAAc;IAAE,SAAS,OAAO;IAAS,QAAQ,OAAO;GAAO;EACtE;EACA,OAAO,KAAK;CACd;CAEA,IAAI,UAAU;EACZ,OAAO,KAAK,MAAM,WAAW,KAAK,IAAI;CACxC;CAEA,IAAI,SAAS;EACX,OAAO,KAAK,MAAM,UAAU,KAAK,IAAI;CACvC;CAEA,IAAI,SAAS;EACX,OAAO,KAAK,MAAM,UAAU,KAAK;CACnC;CAEA,IAAI,aAAa;EACf,OAAO,KAAK,MAAM,cAAc,KAAK;CACvC;CAEA,IAAI,QAAQ;EACV,OAAO,KAAK,MAAM,SAAS,KAAK;CAClC;CAEA,IAAI,SAAiB,MAAmC;EACtD,IAAI,KAAK,SAAS,KAAA,GAAW,OAAO,KAAK,KAAK,IAAI,SAAS,IAAI;EAC/D,IAAI,KAAK,cAAc,SAAS,SAAS;EACzC,KAAK,gBAAgB;GAAE,MAAM;GAAS,WAAW,KAAK,cAAc;GAAW;GAAS;EAAK;CAC/F;CAEA,UAAU,KAAa,OAAgB;EACrC,IAAI,KAAK,SAAS,KAAA,GAAW,OAAO,KAAK,KAAK,UAAU,KAAK,KAAK;EAClE,KAAK,kBAAkB,IAAI,KAAK,KAAK;CACvC;CAEA,MAAM,MAAc,WAAmB,YAAsC;EAC3E,IAAI,KAAK,SAAS,KAAA,GAAW,OAAO,KAAK,KAAK,MAAM,MAAM,WAAW,UAAU;EAC/E,KAAK,MAAM,MAAM,SAAS,KAAK,MAAM,MAAM,WAAW,UAAU,CAAC;CACnE;CAEA,SAAS,OAAuC;EAC9C,IAAI,KAAK,SAAS,KAAA,GAAW,OAAO,KAAK,KAAK,SAAS,KAAK;EAC5D,KAAK,MAAM,MAAM,SAAS,KAAK,SAAS,KAAK,CAAC;CAChD;;CAGA,OAAO,QAAuB;EAC5B,MAAM,SAAS,OAAO,IAAI,KAAK,SAAS,WACtC,kBAAkB,eAAgB,OAAO,QAAQ,SAAU,MAC7D;EACA,MAAM,OAAO,OAAO,KAAK;GAAE,GAAG,KAAK;GAAS;GAAQ,OAAO,KAAK;EAAa,CAAC;EAC9E,KAAK,MAAM,CAAC,KAAK,UAAU,KAAK,mBAAmB,KAAK,UAAU,KAAK,KAAK;EAC5E,KAAK,MAAM,QAAQ,KAAK,OAAO,KAAK,IAAI;EACxC,IAAI,KAAK,cAAc,SAAS,SAC9B,KAAK,IAAI,KAAK,cAAc,SAAS,KAAK,cAAc,IAAI;EAE9D,KAAK,OAAO;CACd;AACF;;;;;;;;;;AAWA,IAAa,iBAAb,MAA4B;CAC1B,WAAiD,CAAC;CAClD,OAA2C,CAAC;CAC5C;;;;;;;;;;CAWA,SACE,SAC0C;EAC1C,OAAO,IAAI,IACT,MAAM,KAAK,UAAU,WACnB,WAAW,OAAO,eACd,SACA,OAAO,MAAwB,YAAY;GACzC,IAAI,KAAK,SAAS,KAAA,GAAW,OAAO,OAAO,IAAI,OAAO;GACtD,MAAM,OAAO;IAAE,GAAG;IAAS,OAAO,SAAS,QAAQ,KAAK;GAAE;GAC1D,KAAK,KAAK,WAAW,OAAO,IAAI,IAAI,CAAC;EACvC,CAAC,CACP,CACF;CACF;CAEA,SAAiC,OAAO,KAAK,EAC3C,OAAO,YAAY;EACjB,IAAI,KAAK,SAAS,KAAA,GAAW,OAAO,KAAK,KAAK,KAAK,OAAO;EAC1D,MAAM,OAAO,IAAI,aAAa,OAAO;EACrC,KAAK,SAAS,KAAK,IAAI;EACvB,OAAO;CACT,EACF,CAAC;;;;;;;;;;CAWD,OAAO,UAAkC;EACvC,IAAI,KAAK,SAAS,KAAA,GAAW;EAC7B,KAAK,OAAO,QAAQ,IAAI,UAAU,OAAO,MAAM;EAC/C,KAAK,MAAM,QAAQ,KAAK,UAAU,KAAK,OAAO,KAAK,IAAI;EACvD,KAAK,SAAS,SAAS;EACvB,KAAK,MAAM,OAAO,KAAK,MAAM,IAAI;EACjC,KAAK,KAAK,SAAS;CACrB;AACF;;;;;;;AAQA,MAAM,YAAkB,UACtB,OAAO,OAAO,OAAO;CACnB,SAAS,EAAE,OAAO,MAAM,QAAQ;CAChC,OAAO,EAAE,OAAO,MAAM,MAAM;AAC9B,CAAC"}
@@ -0,0 +1,92 @@
1
+ import { Layer, Logger } from "effect";
2
+ declare namespace CloudLogging_d_exports {
3
+ export { layer, logger };
4
+ }
5
+ /**
6
+ * The Cloud Logging logger: writes each entry as one JSON line, the format
7
+ * Firebase's own `logger` writes, to `console.error` for Error and Fatal and
8
+ * to `console.log` for everything else.
9
+ *
10
+ * **When to use**
11
+ *
12
+ * Use with `Logger.layer` to combine it with other loggers. To replace
13
+ * Effect's default logger with it, use {@link layer}.
14
+ *
15
+ * **Details**
16
+ *
17
+ * Each entry is a JSON object with these fields:
18
+ *
19
+ * - `severity`: Trace and Debug are `DEBUG`, Info is `INFO`, Warn is
20
+ * `WARNING`, Error is `ERROR` and Fatal is `CRITICAL`.
21
+ * - `message`: the first value logged. A non-empty Cause is appended as
22
+ * `Cause.pretty` output, with its stacks, so Error Reporting picks up
23
+ * defects.
24
+ * - `args`: the other values logged, if any.
25
+ * - `spans`: the log spans, as `{ label: milliseconds }`, if any.
26
+ * - `logging.googleapis.com/trace`, `logging.googleapis.com/spanId` and
27
+ * `logging.googleapis.com/trace_sampled`: the trace of the fiber's current
28
+ * span, with the project from `GCLOUD_PROJECT`, else `GOOGLE_CLOUD_PROJECT`.
29
+ * Left out when there is no span, and when neither variable is set, since a
30
+ * span id means nothing to Cloud Logging without its trace. Cloud Functions
31
+ * sets `GCLOUD_PROJECT`.
32
+ * - Every log annotation, as a field of its own. `severity`, `message` and the
33
+ * `logging.googleapis.com/` fields win over an annotation of the same name.
34
+ *
35
+ * The trace comes from the fiber, never from Firebase's `traceContext`, so
36
+ * concurrent requests on one instance each log under their own trace. Lines go
37
+ * to the `Console` service, which is the global `console` unless you provide
38
+ * another. Entries carry no uid: add one with `Effect.annotateLogs`.
39
+ *
40
+ * @example Logging under a uid
41
+ * ```ts
42
+ * import { Effect } from "effect";
43
+ * import { CloudLogging } from "@visiolab/effect-firebase-functions/logger";
44
+ *
45
+ * const program = Effect.log("Order placed").pipe(
46
+ * Effect.annotateLogs({ uid: "user-1" }),
47
+ * Effect.provide(CloudLogging.layer),
48
+ * );
49
+ * ```
50
+ *
51
+ * @see {@link layer} for replacing Effect's default logger with it
52
+ *
53
+ * @stability unstable
54
+ * @category loggers
55
+ * @since 0.3.0
56
+ */
57
+ export declare const logger: Logger.Logger<unknown, void>;
58
+ /**
59
+ * Replaces the current loggers with the Cloud Logging {@link logger} and
60
+ * Effect's `Logger.tracerLogger`, which records logs as span events.
61
+ *
62
+ * **When to use**
63
+ *
64
+ * Use to log as Cloud Logging entries outside a Function deployed with
65
+ * `Functions`, in any Effect program that runs on Google Cloud. Deployed
66
+ * Functions already log this way.
67
+ *
68
+ * **Details**
69
+ *
70
+ * Provided in the Layer of a deployed Function, it changes nothing. To log
71
+ * differently there, provide your own `Logger.layer`, which wins over this one.
72
+ *
73
+ * @example Logging from a scheduled job
74
+ * ```ts
75
+ * import { Effect } from "effect";
76
+ * import { CloudLogging } from "@visiolab/effect-firebase-functions/logger";
77
+ *
78
+ * const job = Effect.log("Cleaning up").pipe(
79
+ * Effect.withSpan("cleanup"),
80
+ * Effect.provide(CloudLogging.layer),
81
+ * );
82
+ * ```
83
+ *
84
+ * @see {@link logger} for the logger on its own
85
+ *
86
+ * @stability unstable
87
+ * @category layers
88
+ * @since 0.3.0
89
+ */
90
+ export declare const layer: Layer.Layer<never>;
91
+ export { CloudLogging_d_exports };
92
+ //# sourceMappingURL=CloudLogging.d.mts.map
@@ -0,0 +1,146 @@
1
+ import { __exportAll } from "../_virtual/_rolldown/runtime.mjs";
2
+ import { Cause, Console, Formatter, Inspectable, Logger, References } from "effect";
3
+ // src/logger/CloudLogging.ts
4
+ /**
5
+ * A Logger that writes Effect logs as Cloud Logging structured entries, one
6
+ * JSON line each, tied to the trace of the fiber's current span.
7
+ *
8
+ * `Functions` installs it for every call or event, beneath the Layer you
9
+ * serve, so you need this module only to log the same way elsewhere, or to
10
+ * change how a deployed Function logs.
11
+ *
12
+ * @stability unstable
13
+ * @since 0.3.0
14
+ */
15
+ var CloudLogging_exports = /* @__PURE__ */ __exportAll({
16
+ layer: () => layer,
17
+ logger: () => logger
18
+ });
19
+ const severities = {
20
+ All: "DEFAULT",
21
+ Trace: "DEBUG",
22
+ Debug: "DEBUG",
23
+ Info: "INFO",
24
+ Warn: "WARNING",
25
+ Error: "ERROR",
26
+ Fatal: "CRITICAL",
27
+ None: "DEFAULT"
28
+ };
29
+ /**
30
+ * The Cloud Logging logger: writes each entry as one JSON line, the format
31
+ * Firebase's own `logger` writes, to `console.error` for Error and Fatal and
32
+ * to `console.log` for everything else.
33
+ *
34
+ * **When to use**
35
+ *
36
+ * Use with `Logger.layer` to combine it with other loggers. To replace
37
+ * Effect's default logger with it, use {@link layer}.
38
+ *
39
+ * **Details**
40
+ *
41
+ * Each entry is a JSON object with these fields:
42
+ *
43
+ * - `severity`: Trace and Debug are `DEBUG`, Info is `INFO`, Warn is
44
+ * `WARNING`, Error is `ERROR` and Fatal is `CRITICAL`.
45
+ * - `message`: the first value logged. A non-empty Cause is appended as
46
+ * `Cause.pretty` output, with its stacks, so Error Reporting picks up
47
+ * defects.
48
+ * - `args`: the other values logged, if any.
49
+ * - `spans`: the log spans, as `{ label: milliseconds }`, if any.
50
+ * - `logging.googleapis.com/trace`, `logging.googleapis.com/spanId` and
51
+ * `logging.googleapis.com/trace_sampled`: the trace of the fiber's current
52
+ * span, with the project from `GCLOUD_PROJECT`, else `GOOGLE_CLOUD_PROJECT`.
53
+ * Left out when there is no span, and when neither variable is set, since a
54
+ * span id means nothing to Cloud Logging without its trace. Cloud Functions
55
+ * sets `GCLOUD_PROJECT`.
56
+ * - Every log annotation, as a field of its own. `severity`, `message` and the
57
+ * `logging.googleapis.com/` fields win over an annotation of the same name.
58
+ *
59
+ * The trace comes from the fiber, never from Firebase's `traceContext`, so
60
+ * concurrent requests on one instance each log under their own trace. Lines go
61
+ * to the `Console` service, which is the global `console` unless you provide
62
+ * another. Entries carry no uid: add one with `Effect.annotateLogs`.
63
+ *
64
+ * @example Logging under a uid
65
+ * ```ts
66
+ * import { Effect } from "effect";
67
+ * import { CloudLogging } from "@visiolab/effect-firebase-functions/logger";
68
+ *
69
+ * const program = Effect.log("Order placed").pipe(
70
+ * Effect.annotateLogs({ uid: "user-1" }),
71
+ * Effect.provide(CloudLogging.layer),
72
+ * );
73
+ * ```
74
+ *
75
+ * @see {@link layer} for replacing Effect's default logger with it
76
+ *
77
+ * @stability unstable
78
+ * @category loggers
79
+ * @since 0.3.0
80
+ */
81
+ const logger = Logger.make(({ cause, date, fiber, logLevel, message }) => {
82
+ const values = Array.isArray(message) ? message : [message];
83
+ const [first, ...args] = values;
84
+ let text = values.length === 0 ? "" : typeof first === "string" ? first : Inspectable.toStringUnknown(first);
85
+ if (cause.reasons.length > 0) {
86
+ const pretty = Cause.pretty(cause);
87
+ text = text === "" ? pretty : `${text}\n${pretty}`;
88
+ }
89
+ const entry = { ...fiber.getRef(References.CurrentLogAnnotations) };
90
+ if (args.length > 0) entry["args"] = args;
91
+ const logSpans = fiber.getRef(References.CurrentLogSpans);
92
+ if (logSpans.length > 0) {
93
+ const now = date.getTime();
94
+ entry["spans"] = Object.fromEntries(logSpans.map(([label, start]) => [label, now - start]));
95
+ }
96
+ for (const key of Object.keys(entry)) if (key.startsWith("logging.googleapis.com/")) delete entry[key];
97
+ entry["severity"] = severities[logLevel];
98
+ entry["message"] = text;
99
+ const span = fiber.cache.span;
100
+ const project = process.env["GCLOUD_PROJECT"] ?? process.env["GOOGLE_CLOUD_PROJECT"];
101
+ if (span !== void 0 && project !== void 0 && project !== "") {
102
+ entry["logging.googleapis.com/trace"] = `projects/${project}/traces/${span.traceId}`;
103
+ entry["logging.googleapis.com/spanId"] = span.spanId;
104
+ entry["logging.googleapis.com/trace_sampled"] = span.sampled;
105
+ }
106
+ const console = fiber.getRef(Console.Console);
107
+ const line = Formatter.formatJson(entry);
108
+ if (logLevel === "Error" || logLevel === "Fatal") console.error(line);
109
+ else console.log(line);
110
+ });
111
+ /**
112
+ * Replaces the current loggers with the Cloud Logging {@link logger} and
113
+ * Effect's `Logger.tracerLogger`, which records logs as span events.
114
+ *
115
+ * **When to use**
116
+ *
117
+ * Use to log as Cloud Logging entries outside a Function deployed with
118
+ * `Functions`, in any Effect program that runs on Google Cloud. Deployed
119
+ * Functions already log this way.
120
+ *
121
+ * **Details**
122
+ *
123
+ * Provided in the Layer of a deployed Function, it changes nothing. To log
124
+ * differently there, provide your own `Logger.layer`, which wins over this one.
125
+ *
126
+ * @example Logging from a scheduled job
127
+ * ```ts
128
+ * import { Effect } from "effect";
129
+ * import { CloudLogging } from "@visiolab/effect-firebase-functions/logger";
130
+ *
131
+ * const job = Effect.log("Cleaning up").pipe(
132
+ * Effect.withSpan("cleanup"),
133
+ * Effect.provide(CloudLogging.layer),
134
+ * );
135
+ * ```
136
+ *
137
+ * @see {@link logger} for the logger on its own
138
+ *
139
+ * @stability unstable
140
+ * @category layers
141
+ * @since 0.3.0
142
+ */
143
+ const layer = Logger.layer([logger, Logger.tracerLogger]);
144
+ export { CloudLogging_exports, layer, logger };
145
+
146
+ //# sourceMappingURL=CloudLogging.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CloudLogging.mjs","names":[],"sources":["../../src/logger/CloudLogging.ts"],"sourcesContent":["/**\n * A Logger that writes Effect logs as Cloud Logging structured entries, one\n * JSON line each, tied to the trace of the fiber's current span.\n *\n * `Functions` installs it for every call or event, beneath the Layer you\n * serve, so you need this module only to log the same way elsewhere, or to\n * change how a deployed Function logs.\n *\n * @stability unstable\n * @since 0.3.0\n */\nimport { Cause, Console, Formatter, Inspectable, type Layer, Logger, References } from \"effect\";\nimport type * as LogLevel from \"effect/LogLevel\";\n\nconst severities: Record<LogLevel.LogLevel, string> = {\n All: \"DEFAULT\",\n Trace: \"DEBUG\",\n Debug: \"DEBUG\",\n Info: \"INFO\",\n Warn: \"WARNING\",\n Error: \"ERROR\",\n Fatal: \"CRITICAL\",\n None: \"DEFAULT\",\n};\n\n/**\n * The Cloud Logging logger: writes each entry as one JSON line, the format\n * Firebase's own `logger` writes, to `console.error` for Error and Fatal and\n * to `console.log` for everything else.\n *\n * **When to use**\n *\n * Use with `Logger.layer` to combine it with other loggers. To replace\n * Effect's default logger with it, use {@link layer}.\n *\n * **Details**\n *\n * Each entry is a JSON object with these fields:\n *\n * - `severity`: Trace and Debug are `DEBUG`, Info is `INFO`, Warn is\n * `WARNING`, Error is `ERROR` and Fatal is `CRITICAL`.\n * - `message`: the first value logged. A non-empty Cause is appended as\n * `Cause.pretty` output, with its stacks, so Error Reporting picks up\n * defects.\n * - `args`: the other values logged, if any.\n * - `spans`: the log spans, as `{ label: milliseconds }`, if any.\n * - `logging.googleapis.com/trace`, `logging.googleapis.com/spanId` and\n * `logging.googleapis.com/trace_sampled`: the trace of the fiber's current\n * span, with the project from `GCLOUD_PROJECT`, else `GOOGLE_CLOUD_PROJECT`.\n * Left out when there is no span, and when neither variable is set, since a\n * span id means nothing to Cloud Logging without its trace. Cloud Functions\n * sets `GCLOUD_PROJECT`.\n * - Every log annotation, as a field of its own. `severity`, `message` and the\n * `logging.googleapis.com/` fields win over an annotation of the same name.\n *\n * The trace comes from the fiber, never from Firebase's `traceContext`, so\n * concurrent requests on one instance each log under their own trace. Lines go\n * to the `Console` service, which is the global `console` unless you provide\n * another. Entries carry no uid: add one with `Effect.annotateLogs`.\n *\n * @example Logging under a uid\n * ```ts\n * import { Effect } from \"effect\";\n * import { CloudLogging } from \"@visiolab/effect-firebase-functions/logger\";\n *\n * const program = Effect.log(\"Order placed\").pipe(\n * Effect.annotateLogs({ uid: \"user-1\" }),\n * Effect.provide(CloudLogging.layer),\n * );\n * ```\n *\n * @see {@link layer} for replacing Effect's default logger with it\n *\n * @stability unstable\n * @category loggers\n * @since 0.3.0\n */\nexport const logger: Logger.Logger<unknown, void> = Logger.make(\n ({ cause, date, fiber, logLevel, message }) => {\n const values = Array.isArray(message) ? message : [message];\n const [first, ...args] = values;\n let text =\n values.length === 0\n ? \"\"\n : typeof first === \"string\"\n ? first\n : Inspectable.toStringUnknown(first);\n if (cause.reasons.length > 0) {\n const pretty = Cause.pretty(cause);\n text = text === \"\" ? pretty : `${text}\\n${pretty}`;\n }\n\n const entry: Record<string, unknown> = { ...fiber.getRef(References.CurrentLogAnnotations) };\n if (args.length > 0) entry[\"args\"] = args;\n const logSpans = fiber.getRef(References.CurrentLogSpans);\n if (logSpans.length > 0) {\n const now = date.getTime();\n entry[\"spans\"] = Object.fromEntries(logSpans.map(([label, start]) => [label, now - start]));\n }\n for (const key of Object.keys(entry)) {\n if (key.startsWith(\"logging.googleapis.com/\")) delete entry[key];\n }\n entry[\"severity\"] = severities[logLevel];\n entry[\"message\"] = text;\n const span = fiber.cache.span;\n const project = process.env[\"GCLOUD_PROJECT\"] ?? process.env[\"GOOGLE_CLOUD_PROJECT\"];\n if (span !== undefined && project !== undefined && project !== \"\") {\n entry[\"logging.googleapis.com/trace\"] = `projects/${project}/traces/${span.traceId}`;\n entry[\"logging.googleapis.com/spanId\"] = span.spanId;\n entry[\"logging.googleapis.com/trace_sampled\"] = span.sampled;\n }\n\n const console = fiber.getRef(Console.Console);\n const line = Formatter.formatJson(entry);\n if (logLevel === \"Error\" || logLevel === \"Fatal\") console.error(line);\n else console.log(line);\n },\n);\n\n/**\n * Replaces the current loggers with the Cloud Logging {@link logger} and\n * Effect's `Logger.tracerLogger`, which records logs as span events.\n *\n * **When to use**\n *\n * Use to log as Cloud Logging entries outside a Function deployed with\n * `Functions`, in any Effect program that runs on Google Cloud. Deployed\n * Functions already log this way.\n *\n * **Details**\n *\n * Provided in the Layer of a deployed Function, it changes nothing. To log\n * differently there, provide your own `Logger.layer`, which wins over this one.\n *\n * @example Logging from a scheduled job\n * ```ts\n * import { Effect } from \"effect\";\n * import { CloudLogging } from \"@visiolab/effect-firebase-functions/logger\";\n *\n * const job = Effect.log(\"Cleaning up\").pipe(\n * Effect.withSpan(\"cleanup\"),\n * Effect.provide(CloudLogging.layer),\n * );\n * ```\n *\n * @see {@link logger} for the logger on its own\n *\n * @stability unstable\n * @category layers\n * @since 0.3.0\n */\nexport const layer: Layer.Layer<never> = Logger.layer([logger, Logger.tracerLogger]);\n"],"mappings":";;;;;;;;;;;;;;;;;;AAcA,MAAM,aAAgD;CACpD,KAAK;CACL,OAAO;CACP,OAAO;CACP,MAAM;CACN,MAAM;CACN,OAAO;CACP,OAAO;CACP,MAAM;AACR;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsDA,MAAa,SAAuC,OAAO,MACxD,EAAE,OAAO,MAAM,OAAO,UAAU,cAAc;CAC7C,MAAM,SAAS,MAAM,QAAQ,OAAO,IAAI,UAAU,CAAC,OAAO;CAC1D,MAAM,CAAC,OAAO,GAAG,QAAQ;CACzB,IAAI,OACF,OAAO,WAAW,IACd,KACA,OAAO,UAAU,WACf,QACA,YAAY,gBAAgB,KAAK;CACzC,IAAI,MAAM,QAAQ,SAAS,GAAG;EAC5B,MAAM,SAAS,MAAM,OAAO,KAAK;EACjC,OAAO,SAAS,KAAK,SAAS,GAAG,KAAK,IAAI;CAC5C;CAEA,MAAM,QAAiC,EAAE,GAAG,MAAM,OAAO,WAAW,qBAAqB,EAAE;CAC3F,IAAI,KAAK,SAAS,GAAG,MAAM,UAAU;CACrC,MAAM,WAAW,MAAM,OAAO,WAAW,eAAe;CACxD,IAAI,SAAS,SAAS,GAAG;EACvB,MAAM,MAAM,KAAK,QAAQ;EACzB,MAAM,WAAW,OAAO,YAAY,SAAS,KAAK,CAAC,OAAO,WAAW,CAAC,OAAO,MAAM,KAAK,CAAC,CAAC;CAC5F;CACA,KAAK,MAAM,OAAO,OAAO,KAAK,KAAK,GACjC,IAAI,IAAI,WAAW,yBAAyB,GAAG,OAAO,MAAM;CAE9D,MAAM,cAAc,WAAW;CAC/B,MAAM,aAAa;CACnB,MAAM,OAAO,MAAM,MAAM;CACzB,MAAM,UAAU,QAAQ,IAAI,qBAAqB,QAAQ,IAAI;CAC7D,IAAI,SAAS,KAAA,KAAa,YAAY,KAAA,KAAa,YAAY,IAAI;EACjE,MAAM,kCAAkC,YAAY,QAAQ,UAAU,KAAK;EAC3E,MAAM,mCAAmC,KAAK;EAC9C,MAAM,0CAA0C,KAAK;CACvD;CAEA,MAAM,UAAU,MAAM,OAAO,QAAQ,OAAO;CAC5C,MAAM,OAAO,UAAU,WAAW,KAAK;CACvC,IAAI,aAAa,WAAW,aAAa,SAAS,QAAQ,MAAM,IAAI;MAC/D,QAAQ,IAAI,IAAI;AACvB,CACF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkCA,MAAa,QAA4B,OAAO,MAAM,CAAC,QAAQ,OAAO,YAAY,CAAC"}
@@ -0,0 +1,2 @@
1
+ import { CloudLogging_d_exports } from "./CloudLogging.mjs";
2
+ export { CloudLogging_d_exports as CloudLogging };
@@ -0,0 +1 @@
1
+ export { CloudLogging_exports as CloudLogging } from "./CloudLogging.mjs";
package/package.json CHANGED
@@ -1,6 +1,67 @@
1
1
  {
2
2
  "name": "@visiolab/effect-firebase-functions",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "0.3.1",
4
+ "description": "Cloud Functions for Firebase deployment of the @visiolab/effect-firebase Functions.",
5
+ "keywords": [
6
+ "effect",
7
+ "firebase",
8
+ "firebase-functions",
9
+ "typescript"
10
+ ],
11
+ "homepage": "https://visiolab.github.io/effect-firebase/",
12
+ "bugs": {
13
+ "url": "https://github.com/VisioLab/effect-firebase/issues"
14
+ },
15
+ "license": "MIT",
16
+ "repository": {
17
+ "type": "git",
18
+ "url": "git+https://github.com/VisioLab/effect-firebase.git",
19
+ "directory": "packages/functions"
20
+ },
21
+ "files": [
22
+ "src/**/*.ts",
23
+ "dist/**/*.mjs",
24
+ "dist/**/*.mjs.map",
25
+ "dist/**/*.d.mts",
26
+ "dist/**/*.d.mts.map"
27
+ ],
28
+ "type": "module",
29
+ "sideEffects": [],
30
+ "exports": {
31
+ ".": "./dist/index.mjs",
32
+ "./Functions": "./dist/Functions.mjs",
33
+ "./logger": "./dist/logger/index.mjs",
34
+ "./logger/CloudLogging": "./dist/logger/CloudLogging.mjs",
35
+ "./package.json": "./package.json"
36
+ },
37
+ "publishConfig": {
38
+ "access": "public"
39
+ },
40
+ "devDependencies": {
41
+ "@effect/vitest": "^4.1.0",
42
+ "effect": "4.1.0",
43
+ "firebase": "^13.0.0",
44
+ "firebase-admin": "^14.5.0",
45
+ "firebase-functions": "^7.4.0",
46
+ "typescript": "^7.0.2",
47
+ "vite": "npm:@voidzero-dev/vite-plus-core@1.4.0",
48
+ "vite-plus": "1.4.0",
49
+ "vitest": "5.0.5",
50
+ "@visiolab/effect-firebase-admin": "^0.3.1",
51
+ "@visiolab/effect-firebase": "^0.3.1"
52
+ },
53
+ "peerDependencies": {
54
+ "effect": "~4.1.0",
55
+ "firebase-admin": "^14.5.0",
56
+ "firebase-functions": "^7.4.0",
57
+ "@visiolab/effect-firebase": "^0.3.1",
58
+ "@visiolab/effect-firebase-admin": "^0.3.1"
59
+ },
60
+ "scripts": {
61
+ "build": "vp pack",
62
+ "build-fixture": "vp -C tests/fixture pack",
63
+ "dev": "vp pack --watch",
64
+ "test": "vp test",
65
+ "check": "vp check"
66
+ }
6
67
  }