@visiolab/effect-firebase-admin 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.
- package/LICENSE +21 -0
- package/README.md +73 -2
- package/dist/_virtual/_rolldown/runtime.mjs +12 -0
- package/dist/app/App.d.mts +115 -0
- package/dist/app/App.mjs +145 -0
- package/dist/app/App.mjs.map +1 -0
- package/dist/app/index.d.mts +2 -0
- package/dist/app/index.mjs +1 -0
- package/dist/app/internal/app.mjs +20 -0
- package/dist/app/internal/app.mjs.map +1 -0
- package/dist/auth/Auth.d.mts +167 -0
- package/dist/auth/Auth.mjs +131 -0
- package/dist/auth/Auth.mjs.map +1 -0
- package/dist/auth/index.d.mts +2 -0
- package/dist/auth/index.mjs +1 -0
- package/dist/firestore/AdminFirestore.d.mts +241 -0
- package/dist/firestore/AdminFirestore.mjs +133 -0
- package/dist/firestore/AdminFirestore.mjs.map +1 -0
- package/dist/firestore/AdminFirestoreValue.d.mts +61 -0
- package/dist/firestore/AdminFirestoreValue.mjs +62 -0
- package/dist/firestore/AdminFirestoreValue.mjs.map +1 -0
- package/dist/firestore/index.d.mts +3 -0
- package/dist/firestore/index.mjs +3 -0
- package/dist/firestore/internal/firestore.mjs +135 -0
- package/dist/firestore/internal/firestore.mjs.map +1 -0
- package/dist/firestore/internal/firestoreValue.mjs +56 -0
- package/dist/firestore/internal/firestoreValue.mjs.map +1 -0
- package/package.json +61 -3
- package/src/app/App.ts +158 -0
- package/src/app/index.ts +7 -0
- package/src/app/internal/app.ts +35 -0
- package/src/auth/Auth.ts +258 -0
- package/src/auth/index.ts +7 -0
- package/src/firestore/AdminFirestore.ts +303 -0
- package/src/firestore/AdminFirestoreValue.ts +93 -0
- package/src/firestore/index.ts +14 -0
- package/src/firestore/internal/firestore.ts +238 -0
- package/src/firestore/internal/firestoreValue.ts +85 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 VisioLab GmbH
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,74 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @visiolab/effect-firebase-admin
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
The [`@visiolab/effect-firebase`](https://github.com/VisioLab/effect-firebase/tree/main/packages/effect-firebase) services on the Firebase Admin SDK (`firebase-admin`), for servers. In a browser, use [`@visiolab/effect-firebase-client`](https://github.com/VisioLab/effect-firebase/tree/main/packages/client) instead.
|
|
4
|
+
|
|
5
|
+
```sh
|
|
6
|
+
pnpm add effect @visiolab/effect-firebase @visiolab/effect-firebase-admin firebase-admin
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
## App
|
|
10
|
+
|
|
11
|
+
`App` is the Admin SDK app as an Effect service. Every other service in this package is built on it.
|
|
12
|
+
|
|
13
|
+
`App.layer` initializes the app when the layer is built and deletes it when the layer's scope closes. It takes the arguments of `initializeApp(options, name)`. With no arguments, the Admin SDK initializes the app from `FIREBASE_CONFIG` and application default credentials:
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
import { Effect } from "effect";
|
|
17
|
+
import { App } from "@visiolab/effect-firebase-admin/app";
|
|
18
|
+
|
|
19
|
+
const program = Effect.gen(function* () {
|
|
20
|
+
const app = yield* App.App;
|
|
21
|
+
yield* Effect.log(`Running against ${app.name}`);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
program.pipe(Effect.provide(App.layer()));
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
An app already registered under the same name is not deleted when the scope closes. The Admin SDK only hands that app back when the request matches how it was created, though: it rejects auto-init against explicit options, and rejects reuse outright when either side carries a credential or an `httpAgent`. To use an app the host initialized, provide it directly:
|
|
28
|
+
|
|
29
|
+
```ts
|
|
30
|
+
import { Layer } from "effect";
|
|
31
|
+
import { getApp } from "firebase-admin/app";
|
|
32
|
+
import { App } from "@visiolab/effect-firebase-admin/app";
|
|
33
|
+
|
|
34
|
+
const AppLive = Layer.succeed(App.App, getApp());
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Firestore
|
|
38
|
+
|
|
39
|
+
`AdminFirestore.layer` provides one service under two keys: the portable `Firestore`, which code that runs on either SDK asks for, and `AdminFirestore`, which adds what only the Admin SDK can do. Both reach the same database:
|
|
40
|
+
|
|
41
|
+
```ts
|
|
42
|
+
import { Layer } from "effect";
|
|
43
|
+
import { App } from "@visiolab/effect-firebase-admin/app";
|
|
44
|
+
import { AdminFirestore } from "@visiolab/effect-firebase-admin/firestore";
|
|
45
|
+
|
|
46
|
+
const FirestoreLive = AdminFirestore.layer().pipe(Layer.provide(App.layer()));
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Where it differs from the client SDK's:
|
|
50
|
+
|
|
51
|
+
- **The emulator comes from the environment.** The Admin SDK reads `FIRESTORE_EMULATOR_HOST`, which must be set before the database is opened and applies to every app in the process.
|
|
52
|
+
- **Transactions can read many documents.** A transaction body that asks for `AdminFirestore.AdminTransaction` can `getDocs` a collection or a Collection Group.
|
|
53
|
+
- **`preferRest`** talks HTTP/1.1 instead of gRPC until something needs gRPC, which saves start-up time in short-lived processes.
|
|
54
|
+
|
|
55
|
+
`AdminFirestore.instance` is the Admin SDK's own `Firestore`, for what the service does not wrap. `AdminFirestoreValue` maps between Firestore Values and the Admin SDK's values, to read or write a model through it.
|
|
56
|
+
|
|
57
|
+
## Auth
|
|
58
|
+
|
|
59
|
+
`Auth.layer` provides Firebase Authentication for the app in context. It wraps the calls server code uses most, such as `verifyIdToken` and the user management calls. Each fails with an `AuthError` that carries the SDK's `code`, such as `auth/user-not-found`. `Auth.use` reaches the rest of the Admin SDK:
|
|
60
|
+
|
|
61
|
+
```ts
|
|
62
|
+
import { Effect, Layer } from "effect";
|
|
63
|
+
import { App } from "@visiolab/effect-firebase-admin/app";
|
|
64
|
+
import { Auth } from "@visiolab/effect-firebase-admin/auth";
|
|
65
|
+
|
|
66
|
+
const makeAdmin = (idToken: string) =>
|
|
67
|
+
Effect.gen(function* () {
|
|
68
|
+
const auth = yield* Auth.Auth;
|
|
69
|
+
const { uid } = yield* auth.verifyIdToken(idToken, true);
|
|
70
|
+
yield* auth.use((sdk) => sdk.setCustomUserClaims(uid, { admin: true }));
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
const AuthLive = Auth.layer.pipe(Layer.provide(App.layer()));
|
|
74
|
+
```
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// \0rolldown/runtime.js
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __exportAll = (all, no_symbols) => {
|
|
4
|
+
let target = {};
|
|
5
|
+
for (var name in all) __defProp(target, name, {
|
|
6
|
+
get: all[name],
|
|
7
|
+
enumerable: true
|
|
8
|
+
});
|
|
9
|
+
if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
|
|
10
|
+
return target;
|
|
11
|
+
};
|
|
12
|
+
export { __exportAll };
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { Context, Effect, Layer, Schema } from "effect";
|
|
2
|
+
import * as Admin from "firebase-admin/app";
|
|
3
|
+
declare namespace App_d_exports {
|
|
4
|
+
export { App, AppError, layer, make };
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* A Firebase Admin SDK app: the SDK's own `App`.
|
|
8
|
+
*
|
|
9
|
+
* @stability unstable
|
|
10
|
+
* @category models
|
|
11
|
+
* @since 0.1.0
|
|
12
|
+
*/
|
|
13
|
+
export interface App extends Admin.App {}
|
|
14
|
+
/**
|
|
15
|
+
* Service key for the Firebase Admin SDK app.
|
|
16
|
+
*
|
|
17
|
+
* **When to use**
|
|
18
|
+
*
|
|
19
|
+
* Use to provide an app the host already initialized, such as one from
|
|
20
|
+
* `getApp()`, with `Layer.succeed`. Nothing deletes an app provided this way.
|
|
21
|
+
*
|
|
22
|
+
* @example Providing an app the host initialized
|
|
23
|
+
* ```ts
|
|
24
|
+
* import { Layer } from "effect";
|
|
25
|
+
* import { getApp } from "firebase-admin/app";
|
|
26
|
+
* import { App } from "@visiolab/effect-firebase-admin/app";
|
|
27
|
+
*
|
|
28
|
+
* const AppLive = Layer.succeed(App.App, getApp());
|
|
29
|
+
* ```
|
|
30
|
+
*
|
|
31
|
+
* @see {@link layer} for initializing the app for the lifetime of a layer
|
|
32
|
+
*
|
|
33
|
+
* @stability unstable
|
|
34
|
+
* @category services
|
|
35
|
+
* @since 0.1.0
|
|
36
|
+
*/
|
|
37
|
+
export declare const App: Context.Service<App, App>;
|
|
38
|
+
declare const AppError_base: Schema.Class<AppError, Schema.TaggedStruct<"AppError", {
|
|
39
|
+
readonly message: Schema.String;
|
|
40
|
+
readonly cause: Schema.optional<Schema.Defect>;
|
|
41
|
+
}>, import("effect/Cause").YieldableError & {
|
|
42
|
+
readonly brand: unique symbol;
|
|
43
|
+
}>;
|
|
44
|
+
/**
|
|
45
|
+
* Fails {@link make} and {@link layer} when the Admin SDK refuses to initialize
|
|
46
|
+
* the app, with what it threw as the `cause`.
|
|
47
|
+
*
|
|
48
|
+
* @stability unstable
|
|
49
|
+
* @category errors
|
|
50
|
+
* @since 0.1.0
|
|
51
|
+
*/
|
|
52
|
+
export declare class AppError extends AppError_base {}
|
|
53
|
+
/**
|
|
54
|
+
* Initializes a Firebase Admin SDK app, and deletes it again when the
|
|
55
|
+
* surrounding scope closes.
|
|
56
|
+
*
|
|
57
|
+
* **When to use**
|
|
58
|
+
*
|
|
59
|
+
* Use to build the app inside an effect of your own. Most programs provide it
|
|
60
|
+
* with {@link layer} instead.
|
|
61
|
+
*
|
|
62
|
+
* **Details**
|
|
63
|
+
*
|
|
64
|
+
* Takes the arguments of `initializeApp(options, name)`. With no options, the
|
|
65
|
+
* Admin SDK initializes the app from the `FIREBASE_CONFIG` environment variable
|
|
66
|
+
* and application default credentials.
|
|
67
|
+
*
|
|
68
|
+
* An app already registered under the same name is not deleted when the scope
|
|
69
|
+
* closes. The Admin SDK only hands back that app when the request matches how
|
|
70
|
+
* it was created: it rejects auto-init against explicit options, and rejects
|
|
71
|
+
* reuse outright when either side carries a credential or an `httpAgent`. To
|
|
72
|
+
* pick up an app the host initialized, provide `getApp()` as {@link App}.
|
|
73
|
+
*
|
|
74
|
+
* Fails with an {@link AppError} when the Admin SDK refuses to initialize the
|
|
75
|
+
* app. A failure to delete the app is logged as a warning, not raised.
|
|
76
|
+
*
|
|
77
|
+
* @see {@link layer} for the same as a layer
|
|
78
|
+
*
|
|
79
|
+
* @stability unstable
|
|
80
|
+
* @category constructors
|
|
81
|
+
* @since 0.1.0
|
|
82
|
+
*/
|
|
83
|
+
export declare const make: (options?: Admin.AppOptions | undefined, name?: string | undefined) => Effect.Effect<App, AppError, import("effect/Scope").Scope>;
|
|
84
|
+
/**
|
|
85
|
+
* A layer that initializes a Firebase Admin SDK app for the lifetime of the
|
|
86
|
+
* layer.
|
|
87
|
+
*
|
|
88
|
+
* **When to use**
|
|
89
|
+
*
|
|
90
|
+
* Use when this program owns the app. Where the host has already initialized
|
|
91
|
+
* one, provide that app as {@link App} instead.
|
|
92
|
+
*
|
|
93
|
+
* **Details**
|
|
94
|
+
*
|
|
95
|
+
* Takes the arguments of `initializeApp(options, name)`. With no options, the
|
|
96
|
+
* Admin SDK initializes the app from the `FIREBASE_CONFIG` environment variable
|
|
97
|
+
* and application default credentials.
|
|
98
|
+
*
|
|
99
|
+
* @example Initializing a named app with explicit options
|
|
100
|
+
* ```ts
|
|
101
|
+
* import { App } from "@visiolab/effect-firebase-admin/app";
|
|
102
|
+
*
|
|
103
|
+
* const AppLive = App.layer({ projectId: "my-project" }, "secondary");
|
|
104
|
+
* ```
|
|
105
|
+
*
|
|
106
|
+
* @see {@link make} for how the app is initialized and torn down
|
|
107
|
+
* @see {@link App} for providing an app the host initialized
|
|
108
|
+
*
|
|
109
|
+
* @stability unstable
|
|
110
|
+
* @category layers
|
|
111
|
+
* @since 0.1.0
|
|
112
|
+
*/
|
|
113
|
+
export declare const layer: (options?: Admin.AppOptions, name?: string) => Layer.Layer<App, AppError>;
|
|
114
|
+
export { App_d_exports };
|
|
115
|
+
//# sourceMappingURL=App.d.mts.map
|
package/dist/app/App.mjs
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import { __exportAll } from "../_virtual/_rolldown/runtime.mjs";
|
|
2
|
+
import { initialize, release } from "./internal/app.mjs";
|
|
3
|
+
import { Context, Effect, Layer, Schema } from "effect";
|
|
4
|
+
// src/app/App.ts
|
|
5
|
+
/**
|
|
6
|
+
* The Firebase Admin SDK app as an Effect service.
|
|
7
|
+
*
|
|
8
|
+
* Every other service in this package, such as {@link Auth.Auth} and
|
|
9
|
+
* {@link AdminFirestore.AdminFirestore}, is built on the {@link App} in
|
|
10
|
+
* context. Provide it with {@link layer}, or hand it an app the host already
|
|
11
|
+
* initialized.
|
|
12
|
+
*
|
|
13
|
+
* @example Initializing the app for a program
|
|
14
|
+
* ```ts
|
|
15
|
+
* import { Effect } from "effect";
|
|
16
|
+
* import { App } from "@visiolab/effect-firebase-admin/app";
|
|
17
|
+
*
|
|
18
|
+
* const program = Effect.gen(function* () {
|
|
19
|
+
* const app = yield* App.App;
|
|
20
|
+
* yield* Effect.log(`Running against ${app.name}`);
|
|
21
|
+
* });
|
|
22
|
+
*
|
|
23
|
+
* program.pipe(Effect.provide(App.layer()));
|
|
24
|
+
* ```
|
|
25
|
+
*
|
|
26
|
+
* @see [Add the Firebase Admin SDK to your server](https://firebase.google.com/docs/admin/setup)
|
|
27
|
+
*
|
|
28
|
+
* @stability unstable
|
|
29
|
+
* @since 0.1.0
|
|
30
|
+
*/
|
|
31
|
+
var App_exports = /* @__PURE__ */ __exportAll({
|
|
32
|
+
App: () => App,
|
|
33
|
+
AppError: () => AppError,
|
|
34
|
+
layer: () => layer,
|
|
35
|
+
make: () => make
|
|
36
|
+
});
|
|
37
|
+
/**
|
|
38
|
+
* Service key for the Firebase Admin SDK app.
|
|
39
|
+
*
|
|
40
|
+
* **When to use**
|
|
41
|
+
*
|
|
42
|
+
* Use to provide an app the host already initialized, such as one from
|
|
43
|
+
* `getApp()`, with `Layer.succeed`. Nothing deletes an app provided this way.
|
|
44
|
+
*
|
|
45
|
+
* @example Providing an app the host initialized
|
|
46
|
+
* ```ts
|
|
47
|
+
* import { Layer } from "effect";
|
|
48
|
+
* import { getApp } from "firebase-admin/app";
|
|
49
|
+
* import { App } from "@visiolab/effect-firebase-admin/app";
|
|
50
|
+
*
|
|
51
|
+
* const AppLive = Layer.succeed(App.App, getApp());
|
|
52
|
+
* ```
|
|
53
|
+
*
|
|
54
|
+
* @see {@link layer} for initializing the app for the lifetime of a layer
|
|
55
|
+
*
|
|
56
|
+
* @stability unstable
|
|
57
|
+
* @category services
|
|
58
|
+
* @since 0.1.0
|
|
59
|
+
*/
|
|
60
|
+
const App = Context.Service("@visiolab/effect-firebase-admin/app/App");
|
|
61
|
+
/**
|
|
62
|
+
* Fails {@link make} and {@link layer} when the Admin SDK refuses to initialize
|
|
63
|
+
* the app, with what it threw as the `cause`.
|
|
64
|
+
*
|
|
65
|
+
* @stability unstable
|
|
66
|
+
* @category errors
|
|
67
|
+
* @since 0.1.0
|
|
68
|
+
*/
|
|
69
|
+
var AppError = class extends Schema.TaggedError()("AppError", {
|
|
70
|
+
message: Schema.String,
|
|
71
|
+
cause: Schema.optional(Schema.Defect())
|
|
72
|
+
}) {};
|
|
73
|
+
/**
|
|
74
|
+
* Initializes a Firebase Admin SDK app, and deletes it again when the
|
|
75
|
+
* surrounding scope closes.
|
|
76
|
+
*
|
|
77
|
+
* **When to use**
|
|
78
|
+
*
|
|
79
|
+
* Use to build the app inside an effect of your own. Most programs provide it
|
|
80
|
+
* with {@link layer} instead.
|
|
81
|
+
*
|
|
82
|
+
* **Details**
|
|
83
|
+
*
|
|
84
|
+
* Takes the arguments of `initializeApp(options, name)`. With no options, the
|
|
85
|
+
* Admin SDK initializes the app from the `FIREBASE_CONFIG` environment variable
|
|
86
|
+
* and application default credentials.
|
|
87
|
+
*
|
|
88
|
+
* An app already registered under the same name is not deleted when the scope
|
|
89
|
+
* closes. The Admin SDK only hands back that app when the request matches how
|
|
90
|
+
* it was created: it rejects auto-init against explicit options, and rejects
|
|
91
|
+
* reuse outright when either side carries a credential or an `httpAgent`. To
|
|
92
|
+
* pick up an app the host initialized, provide `getApp()` as {@link App}.
|
|
93
|
+
*
|
|
94
|
+
* Fails with an {@link AppError} when the Admin SDK refuses to initialize the
|
|
95
|
+
* app. A failure to delete the app is logged as a warning, not raised.
|
|
96
|
+
*
|
|
97
|
+
* @see {@link layer} for the same as a layer
|
|
98
|
+
*
|
|
99
|
+
* @stability unstable
|
|
100
|
+
* @category constructors
|
|
101
|
+
* @since 0.1.0
|
|
102
|
+
*/
|
|
103
|
+
const make = Effect.fn("App.make")(function* (options, name) {
|
|
104
|
+
const { app } = yield* Effect.acquireRelease(Effect.try({
|
|
105
|
+
try: () => initialize(options, name),
|
|
106
|
+
catch: (cause) => new AppError({
|
|
107
|
+
message: "Failed to initialize the Firebase Admin SDK app",
|
|
108
|
+
cause
|
|
109
|
+
})
|
|
110
|
+
}), release);
|
|
111
|
+
return app;
|
|
112
|
+
});
|
|
113
|
+
/**
|
|
114
|
+
* A layer that initializes a Firebase Admin SDK app for the lifetime of the
|
|
115
|
+
* layer.
|
|
116
|
+
*
|
|
117
|
+
* **When to use**
|
|
118
|
+
*
|
|
119
|
+
* Use when this program owns the app. Where the host has already initialized
|
|
120
|
+
* one, provide that app as {@link App} instead.
|
|
121
|
+
*
|
|
122
|
+
* **Details**
|
|
123
|
+
*
|
|
124
|
+
* Takes the arguments of `initializeApp(options, name)`. With no options, the
|
|
125
|
+
* Admin SDK initializes the app from the `FIREBASE_CONFIG` environment variable
|
|
126
|
+
* and application default credentials.
|
|
127
|
+
*
|
|
128
|
+
* @example Initializing a named app with explicit options
|
|
129
|
+
* ```ts
|
|
130
|
+
* import { App } from "@visiolab/effect-firebase-admin/app";
|
|
131
|
+
*
|
|
132
|
+
* const AppLive = App.layer({ projectId: "my-project" }, "secondary");
|
|
133
|
+
* ```
|
|
134
|
+
*
|
|
135
|
+
* @see {@link make} for how the app is initialized and torn down
|
|
136
|
+
* @see {@link App} for providing an app the host initialized
|
|
137
|
+
*
|
|
138
|
+
* @stability unstable
|
|
139
|
+
* @category layers
|
|
140
|
+
* @since 0.1.0
|
|
141
|
+
*/
|
|
142
|
+
const layer = (options, name) => Layer.effect(App, make(options, name));
|
|
143
|
+
export { App, AppError, App_exports, layer, make };
|
|
144
|
+
|
|
145
|
+
//# sourceMappingURL=App.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"App.mjs","names":["internalApp.initialize","internalApp.release"],"sources":["../../src/app/App.ts"],"sourcesContent":["/**\n * The Firebase Admin SDK app as an Effect service.\n *\n * Every other service in this package, such as {@link Auth.Auth} and\n * {@link AdminFirestore.AdminFirestore}, is built on the {@link App} in\n * context. Provide it with {@link layer}, or hand it an app the host already\n * initialized.\n *\n * @example Initializing the app for a program\n * ```ts\n * import { Effect } from \"effect\";\n * import { App } from \"@visiolab/effect-firebase-admin/app\";\n *\n * const program = Effect.gen(function* () {\n * const app = yield* App.App;\n * yield* Effect.log(`Running against ${app.name}`);\n * });\n *\n * program.pipe(Effect.provide(App.layer()));\n * ```\n *\n * @see [Add the Firebase Admin SDK to your server](https://firebase.google.com/docs/admin/setup)\n *\n * @stability unstable\n * @since 0.1.0\n */\nimport { Context, Effect, Layer, Schema } from \"effect\";\nimport type * as Admin from \"firebase-admin/app\";\n// Only the module's documentation links to these.\n// oxlint-disable-next-line no-unused-vars\nimport type * as Auth from \"../auth/Auth.ts\";\n// oxlint-disable-next-line no-unused-vars\nimport type * as AdminFirestore from \"../firestore/AdminFirestore.ts\";\nimport * as internalApp from \"./internal/app.ts\";\n\n/**\n * A Firebase Admin SDK app: the SDK's own `App`.\n *\n * @stability unstable\n * @category models\n * @since 0.1.0\n */\nexport interface App extends Admin.App {}\n\n/**\n * Service key for the Firebase Admin SDK app.\n *\n * **When to use**\n *\n * Use to provide an app the host already initialized, such as one from\n * `getApp()`, with `Layer.succeed`. Nothing deletes an app provided this way.\n *\n * @example Providing an app the host initialized\n * ```ts\n * import { Layer } from \"effect\";\n * import { getApp } from \"firebase-admin/app\";\n * import { App } from \"@visiolab/effect-firebase-admin/app\";\n *\n * const AppLive = Layer.succeed(App.App, getApp());\n * ```\n *\n * @see {@link layer} for initializing the app for the lifetime of a layer\n *\n * @stability unstable\n * @category services\n * @since 0.1.0\n */\nexport const App = Context.Service<App>(\"@visiolab/effect-firebase-admin/app/App\");\n\n/**\n * Fails {@link make} and {@link layer} when the Admin SDK refuses to initialize\n * the app, with what it threw as the `cause`.\n *\n * @stability unstable\n * @category errors\n * @since 0.1.0\n */\nexport class AppError extends Schema.TaggedError<AppError, { readonly brand: unique symbol }>()(\n \"AppError\",\n {\n message: Schema.String,\n cause: Schema.optional(Schema.Defect()),\n },\n) {}\n\n/**\n * Initializes a Firebase Admin SDK app, and deletes it again when the\n * surrounding scope closes.\n *\n * **When to use**\n *\n * Use to build the app inside an effect of your own. Most programs provide it\n * with {@link layer} instead.\n *\n * **Details**\n *\n * Takes the arguments of `initializeApp(options, name)`. With no options, the\n * Admin SDK initializes the app from the `FIREBASE_CONFIG` environment variable\n * and application default credentials.\n *\n * An app already registered under the same name is not deleted when the scope\n * closes. The Admin SDK only hands back that app when the request matches how\n * it was created: it rejects auto-init against explicit options, and rejects\n * reuse outright when either side carries a credential or an `httpAgent`. To\n * pick up an app the host initialized, provide `getApp()` as {@link App}.\n *\n * Fails with an {@link AppError} when the Admin SDK refuses to initialize the\n * app. A failure to delete the app is logged as a warning, not raised.\n *\n * @see {@link layer} for the same as a layer\n *\n * @stability unstable\n * @category constructors\n * @since 0.1.0\n */\nexport const make = Effect.fn(\"App.make\")(function* (options?: Admin.AppOptions, name?: string) {\n const { app } = yield* Effect.acquireRelease(\n Effect.try({\n try: () => internalApp.initialize(options, name),\n catch: (cause) =>\n new AppError({ message: \"Failed to initialize the Firebase Admin SDK app\", cause }),\n }),\n internalApp.release,\n );\n return app;\n});\n\n/**\n * A layer that initializes a Firebase Admin SDK app for the lifetime of the\n * layer.\n *\n * **When to use**\n *\n * Use when this program owns the app. Where the host has already initialized\n * one, provide that app as {@link App} instead.\n *\n * **Details**\n *\n * Takes the arguments of `initializeApp(options, name)`. With no options, the\n * Admin SDK initializes the app from the `FIREBASE_CONFIG` environment variable\n * and application default credentials.\n *\n * @example Initializing a named app with explicit options\n * ```ts\n * import { App } from \"@visiolab/effect-firebase-admin/app\";\n *\n * const AppLive = App.layer({ projectId: \"my-project\" }, \"secondary\");\n * ```\n *\n * @see {@link make} for how the app is initialized and torn down\n * @see {@link App} for providing an app the host initialized\n *\n * @stability unstable\n * @category layers\n * @since 0.1.0\n */\nexport const layer = (options?: Admin.AppOptions, name?: string): Layer.Layer<App, AppError> =>\n Layer.effect(App, make(options, name));\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAmEA,MAAa,MAAM,QAAQ,QAAa,yCAAyC;;;;;;;;;AAUjF,IAAa,WAAb,cAA8B,OAAO,YAAyD,CAAC,CAC7F,YACA;CACE,SAAS,OAAO;CAChB,OAAO,OAAO,SAAS,OAAO,OAAO,CAAC;AACxC,CACF,CAAC,CAAC,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgCH,MAAa,OAAO,OAAO,GAAG,UAAU,CAAC,CAAC,WAAW,SAA4B,MAAe;CAC9F,MAAM,EAAE,QAAQ,OAAO,OAAO,eAC5B,OAAO,IAAI;EACT,WAAWA,WAAuB,SAAS,IAAI;EAC/C,QAAQ,UACN,IAAI,SAAS;GAAE,SAAS;GAAmD;EAAM,CAAC;CACtF,CAAC,GACDC,OACF;CACA,OAAO;AACT,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+BD,MAAa,SAAS,SAA4B,SAChD,MAAM,OAAO,KAAK,KAAK,SAAS,IAAI,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { App_exports as App } from "./App.mjs";
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { Effect } from "effect";
|
|
2
|
+
import { defaultAppName } from "@visiolab/effect-firebase/app/App";
|
|
3
|
+
import * as Admin from "firebase-admin/app";
|
|
4
|
+
// src/app/internal/app.ts
|
|
5
|
+
/** @internal */
|
|
6
|
+
const initialize = (options, name) => {
|
|
7
|
+
const owned = !Admin.getApps().some((app) => app.name === (name ?? defaultAppName));
|
|
8
|
+
return {
|
|
9
|
+
app: Admin.initializeApp(options, name),
|
|
10
|
+
owned
|
|
11
|
+
};
|
|
12
|
+
};
|
|
13
|
+
/** @internal */
|
|
14
|
+
const release = ({ app, owned }) => owned ? Effect.tryPromise(() => Admin.deleteApp(app)).pipe(Effect.ignore({
|
|
15
|
+
log: "Warn",
|
|
16
|
+
message: `Failed to delete the Firebase Admin SDK app "${app.name}"`
|
|
17
|
+
})) : Effect.void;
|
|
18
|
+
export { initialize, release };
|
|
19
|
+
|
|
20
|
+
//# sourceMappingURL=app.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app.mjs","names":[],"sources":["../../../src/app/internal/app.ts"],"sourcesContent":["import { Effect } from \"effect\";\nimport { defaultAppName } from \"@visiolab/effect-firebase/app/App\";\nimport * as Admin from \"firebase-admin/app\";\nimport type * as App from \"../App.ts\";\n\n/**\n * The app together with whether we are the ones who initialized it.\n *\n * @internal\n */\nexport interface Initialized {\n readonly app: App.App;\n readonly owned: boolean;\n}\n\n/** @internal */\nexport const initialize = (options?: Admin.AppOptions, name?: string): Initialized => {\n // An app that is already registered under this name was initialized by\n // somebody else - typically the hosting runtime - so ending its lifecycle is\n // not ours to do. `initializeApp` still throws when that app was configured\n // differently, so conflicting configuration is not swallowed here.\n const owned = !Admin.getApps().some((app) => app.name === (name ?? defaultAppName));\n return { app: Admin.initializeApp(options, name), owned };\n};\n\n/** @internal */\nexport const release = ({ app, owned }: Initialized): Effect.Effect<void> =>\n owned\n ? Effect.tryPromise(() => Admin.deleteApp(app)).pipe(\n Effect.ignore({\n log: \"Warn\",\n message: `Failed to delete the Firebase Admin SDK app \"${app.name}\"`,\n }),\n )\n : Effect.void;\n"],"mappings":";;;;;AAgBA,MAAa,cAAc,SAA4B,SAA+B;CAKpF,MAAM,QAAQ,CAAC,MAAM,QAAQ,CAAC,CAAC,MAAM,QAAQ,IAAI,UAAU,QAAQ,eAAe;CAClF,OAAO;EAAE,KAAK,MAAM,cAAc,SAAS,IAAI;EAAG;CAAM;AAC1D;;AAGA,MAAa,WAAW,EAAE,KAAK,YAC7B,QACI,OAAO,iBAAiB,MAAM,UAAU,GAAG,CAAC,CAAC,CAAC,KAC5C,OAAO,OAAO;CACZ,KAAK;CACL,SAAS,gDAAgD,IAAI,KAAK;AACpE,CAAC,CACH,IACA,OAAO"}
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import { App } from "../app/App.mjs";
|
|
2
|
+
import { Context, Effect, Layer, Schema } from "effect";
|
|
3
|
+
import * as Admin from "firebase-admin/auth";
|
|
4
|
+
declare namespace Auth_d_exports {
|
|
5
|
+
export { Auth, AuthError, layer, make };
|
|
6
|
+
}
|
|
7
|
+
declare const AuthError_base: Schema.Class<AuthError, Schema.TaggedStruct<"AuthError", {
|
|
8
|
+
readonly message: Schema.String;
|
|
9
|
+
/** The `service/string-code` the Admin SDK reported, such as `auth/user-not-found`. */
|
|
10
|
+
readonly code: Schema.optional<Schema.String>;
|
|
11
|
+
readonly cause: Schema.optional<Schema.Defect>;
|
|
12
|
+
}>, import("effect/Cause").YieldableError & {
|
|
13
|
+
readonly brand: unique symbol;
|
|
14
|
+
}>;
|
|
15
|
+
/**
|
|
16
|
+
* Fails a call on {@link Auth} when the Admin SDK rejects it, with what it
|
|
17
|
+
* threw as the `cause`.
|
|
18
|
+
*
|
|
19
|
+
* Match on `code` to tell failures apart, such as `auth/user-not-found` or
|
|
20
|
+
* `auth/id-token-revoked`.
|
|
21
|
+
*
|
|
22
|
+
* @stability unstable
|
|
23
|
+
* @category errors
|
|
24
|
+
* @since 0.1.0
|
|
25
|
+
*/
|
|
26
|
+
export declare class AuthError extends AuthError_base {}
|
|
27
|
+
/**
|
|
28
|
+
* Firebase Authentication for a single app.
|
|
29
|
+
*
|
|
30
|
+
* Each call is the Admin SDK's method of the same name, with the same
|
|
31
|
+
* arguments, failing with an {@link AuthError} and traced as a span named
|
|
32
|
+
* `Auth.<method>`. Use {@link Auth.use} for anything else the Admin SDK offers.
|
|
33
|
+
*
|
|
34
|
+
* The Admin SDK cannot abort a request in flight, so interrupting a call does
|
|
35
|
+
* not stop it: the request still runs to completion.
|
|
36
|
+
*
|
|
37
|
+
* @stability unstable
|
|
38
|
+
* @category models
|
|
39
|
+
* @since 0.1.0
|
|
40
|
+
*/
|
|
41
|
+
export interface Auth {
|
|
42
|
+
/**
|
|
43
|
+
* Runs `f` against the Admin SDK's own `Auth`, failing with an
|
|
44
|
+
* {@link AuthError} when it throws or rejects.
|
|
45
|
+
*
|
|
46
|
+
* **When to use**
|
|
47
|
+
*
|
|
48
|
+
* Use for the Admin SDK calls this service does not wrap, such as
|
|
49
|
+
* `setCustomUserClaims` or `listUsers`. Prefer the wrapped methods where one
|
|
50
|
+
* exists: they name the failure in the error's message.
|
|
51
|
+
*
|
|
52
|
+
* @example Setting custom claims
|
|
53
|
+
* ```ts
|
|
54
|
+
* import { Effect } from "effect";
|
|
55
|
+
* import { Auth } from "@visiolab/effect-firebase-admin/auth";
|
|
56
|
+
*
|
|
57
|
+
* const makeAdmin = (uid: string) =>
|
|
58
|
+
* Effect.gen(function* () {
|
|
59
|
+
* const auth = yield* Auth.Auth;
|
|
60
|
+
* yield* auth.use((sdk) => sdk.setCustomUserClaims(uid, { admin: true }));
|
|
61
|
+
* });
|
|
62
|
+
* ```
|
|
63
|
+
*/
|
|
64
|
+
readonly use: <A>(f: (auth: Admin.Auth) => PromiseLike<A> | A) => Effect.Effect<A, AuthError>;
|
|
65
|
+
/**
|
|
66
|
+
* Verifies a Firebase ID token and decodes it.
|
|
67
|
+
*
|
|
68
|
+
* With `checkRevoked`, it also fails for a token whose session was revoked or
|
|
69
|
+
* whose user is disabled, at the cost of a request to the Auth backend.
|
|
70
|
+
*
|
|
71
|
+
* @see {@link Auth.createCustomToken} for minting a token a client signs in with
|
|
72
|
+
*/
|
|
73
|
+
readonly verifyIdToken: (idToken: string, checkRevoked?: boolean) => Effect.Effect<Admin.DecodedIdToken, AuthError>;
|
|
74
|
+
/**
|
|
75
|
+
* Mints a custom token for `uid`, which a client signs in with through
|
|
76
|
+
* `signInWithCustomToken`.
|
|
77
|
+
*
|
|
78
|
+
* `developerClaims` end up in the ID tokens of that session.
|
|
79
|
+
*
|
|
80
|
+
* @see {@link Auth.verifyIdToken} for checking the ID token a client sends back
|
|
81
|
+
*/
|
|
82
|
+
readonly createCustomToken: (uid: string, developerClaims?: object) => Effect.Effect<string, AuthError>;
|
|
83
|
+
/**
|
|
84
|
+
* Reads the user with `uid`.
|
|
85
|
+
*
|
|
86
|
+
* Fails with `auth/user-not-found` when there is none.
|
|
87
|
+
*
|
|
88
|
+
* @see {@link Auth.getUserByEmail} for looking a user up by email
|
|
89
|
+
*/
|
|
90
|
+
readonly getUser: (uid: string) => Effect.Effect<Admin.UserRecord, AuthError>;
|
|
91
|
+
/**
|
|
92
|
+
* Reads the user with `email`.
|
|
93
|
+
*
|
|
94
|
+
* Fails with `auth/user-not-found` when there is none.
|
|
95
|
+
*
|
|
96
|
+
* @see {@link Auth.getUser} for looking a user up by uid
|
|
97
|
+
*/
|
|
98
|
+
readonly getUserByEmail: (email: string) => Effect.Effect<Admin.UserRecord, AuthError>;
|
|
99
|
+
/**
|
|
100
|
+
* Creates a user, under the `uid` in `properties` or one the Auth backend
|
|
101
|
+
* picks.
|
|
102
|
+
*
|
|
103
|
+
* @see {@link Auth.updateUser} for changing a user that exists
|
|
104
|
+
*/
|
|
105
|
+
readonly createUser: (properties: Admin.CreateRequest) => Effect.Effect<Admin.UserRecord, AuthError>;
|
|
106
|
+
/**
|
|
107
|
+
* Changes the properties of the user with `uid`.
|
|
108
|
+
*
|
|
109
|
+
* @see {@link Auth.createUser} for creating one
|
|
110
|
+
*/
|
|
111
|
+
readonly updateUser: (uid: string, properties: Admin.UpdateRequest) => Effect.Effect<Admin.UserRecord, AuthError>;
|
|
112
|
+
/**
|
|
113
|
+
* Deletes the user with `uid`.
|
|
114
|
+
*/
|
|
115
|
+
readonly deleteUser: (uid: string) => Effect.Effect<void, AuthError>;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Service key for Firebase Authentication.
|
|
119
|
+
*
|
|
120
|
+
* @stability unstable
|
|
121
|
+
* @category services
|
|
122
|
+
* @since 0.1.0
|
|
123
|
+
*/
|
|
124
|
+
export declare const Auth: Context.Service<Auth, Auth>;
|
|
125
|
+
/**
|
|
126
|
+
* Builds {@link Auth} on the {@link App.App} in context.
|
|
127
|
+
*
|
|
128
|
+
* **When to use**
|
|
129
|
+
*
|
|
130
|
+
* Use to build the service inside an effect of your own. Most programs provide
|
|
131
|
+
* it with {@link layer} instead.
|
|
132
|
+
*
|
|
133
|
+
* **Details**
|
|
134
|
+
*
|
|
135
|
+
* Building the service cannot fail. `getAuth` only throws for an app that was
|
|
136
|
+
* already deleted or is not an app at all. Both are wiring mistakes, so they
|
|
137
|
+
* are defects, and {@link AuthError} only ever means that a call failed.
|
|
138
|
+
*
|
|
139
|
+
* @see {@link layer} for the same as a layer
|
|
140
|
+
*
|
|
141
|
+
* @stability unstable
|
|
142
|
+
* @category constructors
|
|
143
|
+
* @since 0.1.0
|
|
144
|
+
*/
|
|
145
|
+
export declare const make: Effect.Effect<Auth, never, App>;
|
|
146
|
+
/**
|
|
147
|
+
* A layer that provides {@link Auth} for the {@link App.App} in context.
|
|
148
|
+
*
|
|
149
|
+
* @example Providing Auth
|
|
150
|
+
* ```ts
|
|
151
|
+
* import { Layer } from "effect";
|
|
152
|
+
* import { App } from "@visiolab/effect-firebase-admin/app";
|
|
153
|
+
* import { Auth } from "@visiolab/effect-firebase-admin/auth";
|
|
154
|
+
*
|
|
155
|
+
* const AuthLive = Auth.layer.pipe(Layer.provide(App.layer()));
|
|
156
|
+
* ```
|
|
157
|
+
*
|
|
158
|
+
* @see {@link make} for why building the service cannot fail
|
|
159
|
+
* @see {@link App.layer} for the app it needs
|
|
160
|
+
*
|
|
161
|
+
* @stability unstable
|
|
162
|
+
* @category layers
|
|
163
|
+
* @since 0.1.0
|
|
164
|
+
*/
|
|
165
|
+
export declare const layer: Layer.Layer<Auth, never, App>;
|
|
166
|
+
export { Auth_d_exports };
|
|
167
|
+
//# sourceMappingURL=Auth.d.mts.map
|