spicedb-embedded 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.
package/README.md ADDED
@@ -0,0 +1,146 @@
1
+ # spicedb-embedded (Node.js)
2
+
3
+ Embedded [SpiceDB](https://authzed.com/spicedb) for Node.js — authorization server for tests and development. Uses the shared/c C library via koffi FFI and connects over gRPC via Unix sockets or TCP. Supports memory (default), postgres, cockroachdb, spanner, and mysql datastores. **Node.js only** (not for browser/frontend).
4
+
5
+ ## Comparison with SpiceDB WASM
6
+
7
+ SpiceDB also provides a [WebAssembly (WASM) build](https://authzed.com/blog/some-assembly-required) used in the [Authzed Playground](https://play.authzed.com). This Node.js implementation differs in several important ways:
8
+
9
+ | Aspect | **spicedb-embedded** (this package) | **SpiceDB WASM** |
10
+ | ---------------- | -------------------------------------------------------------------- | ------------------------------------------------------ |
11
+ | **Runtime** | Node.js only (native addon) | Browser + Node.js (via WASM) |
12
+ | **Architecture** | Spawns real SpiceDB server via C/CGO FFI; full gRPC over Unix socket | Compiled Go→WASM; callback-based development API |
13
+ | **API** | Full SpiceDB gRPC API (Permissions, Schema, Watch, etc.) | Development API only (check, validate, run operations) |
14
+ | **Use case** | Server-side tests, development, local tooling | Browser Playground, client-side validation |
15
+ | **Performance** | Native SpiceDB; full caching, production-grade | No Ristretto cache; simplified for WASM |
16
+ | **Distribution** | Requires platform-specific `libspicedb.so`/`.dylib` | Single `.wasm` file (~25MB) |
17
+
18
+ **When to use this package:** Node.js applications that need an embedded SpiceDB for integration tests, local development, or tooling. You get the real SpiceDB implementation with the full API.
19
+
20
+ **When to use WASM:** Browser-based tools (e.g., schema playground) or when you need a single portable binary.
21
+
22
+ ## Installation
23
+
24
+ ```bash
25
+ npm install spicedb-embedded
26
+ ```
27
+
28
+ Or from source:
29
+
30
+ ```bash
31
+ cd node && npm install
32
+ ```
33
+
34
+ **Prerequisites:** Go 1.23+ with CGO enabled. Build the shared library first:
35
+
36
+ ```bash
37
+ cd shared/c && CGO_ENABLED=1 go build -buildmode=c-shared -o libspicedb.dylib . # macOS
38
+ cd shared/c && CGO_ENABLED=1 go build -buildmode=c-shared -o libspicedb.so . # Linux
39
+ ```
40
+
41
+ The library looks for `libspicedb.dylib` (macOS) or `libspicedb.so` (Linux) in `SPICEDB_LIBRARY_PATH` or relative to the working directory. Override with `SPICEDB_LIBRARY_PATH=/path/to/shared/c`.
42
+
43
+ ## Usage
44
+
45
+ ```typescript
46
+ import { v1, EmbeddedSpiceDB } from "spicedb-embedded";
47
+
48
+ const {
49
+ ObjectReference,
50
+ Relationship,
51
+ SubjectReference,
52
+ CheckPermissionRequest,
53
+ Consistency,
54
+ CheckPermissionResponse,
55
+ CheckPermissionResponse_Permissionship,
56
+ } = v1;
57
+
58
+ const schema = `
59
+ definition user {}
60
+
61
+ definition document {
62
+ relation reader: user
63
+ permission read = reader
64
+ }
65
+ `;
66
+
67
+ const rel = Relationship.create({
68
+ resource: ObjectReference.create({
69
+ objectType: "document",
70
+ objectId: "readme",
71
+ }),
72
+ relation: "reader",
73
+ subject: SubjectReference.create({
74
+ object: ObjectReference.create({ objectType: "user", objectId: "alice" }),
75
+ }),
76
+ });
77
+
78
+ const spicedb = await EmbeddedSpiceDB.create(schema, [rel]);
79
+
80
+ try {
81
+ const response = await spicedb.permissions().promises.checkPermission(
82
+ CheckPermissionRequest.create({
83
+ consistency: Consistency.create({
84
+ requirement: { oneofKind: "fullyConsistent", fullyConsistent: true },
85
+ }),
86
+ resource: ObjectReference.create({
87
+ objectType: "document",
88
+ objectId: "readme",
89
+ }),
90
+ permission: "read",
91
+ subject: SubjectReference.create({
92
+ object: ObjectReference.create({
93
+ objectType: "user",
94
+ objectId: "alice",
95
+ }),
96
+ }),
97
+ })
98
+ );
99
+
100
+ const allowed =
101
+ response.permissionship ===
102
+ CheckPermissionResponse_Permissionship.HAS_PERMISSION;
103
+ } finally {
104
+ spicedb.close();
105
+ }
106
+ ```
107
+
108
+ ## API
109
+
110
+ - **`EmbeddedSpiceDB.create(schema, relationships, options?)`** — Create an instance (async). Pass `[]` for no initial relationships. Pass `SpiceDBStartOptions` for datastore/transport config.
111
+ - **`permissions()`** — Permissions service client (CheckPermission, WriteRelationships, ReadRelationships, etc.). Use `.promises` for async/await.
112
+ - **`schema()`** — Schema service client (ReadSchema, WriteSchema, ReflectSchema, etc.).
113
+ - **`watch()`** — Watch service client for relationship changes.
114
+ - **`close()`** — Dispose the instance and close the channel.
115
+
116
+ Use types from `v1` (re-exported from `@authzed/authzed-node`) for `ObjectReference`, `SubjectReference`, `Relationship`, etc.
117
+
118
+ ### SpiceDBStartOptions
119
+
120
+ ```typescript
121
+ import { EmbeddedSpiceDB, SpiceDBStartOptions } from "spicedb-embedded";
122
+
123
+ const options: SpiceDBStartOptions = {
124
+ datastore: "memory", // or "postgres", "cockroachdb", "spanner", "mysql"
125
+ grpc_transport: "unix", // or "tcp"; default by platform
126
+ datastore_uri: "postgres://user:pass@localhost:5432/spicedb", // required for remote
127
+ spanner_credentials_file: "/path/to/key.json", // Spanner only
128
+ spanner_emulator_host: "localhost:9010", // Spanner emulator
129
+ mysql_table_prefix: "spicedb_", // MySQL only (optional)
130
+ metrics_enabled: false, // default; set true to enable Prometheus metrics
131
+ };
132
+
133
+ const spicedb = await EmbeddedSpiceDB.create(schema, [], options);
134
+ ```
135
+
136
+ ## Building & Testing
137
+
138
+ ```bash
139
+ mise run shared-c-build
140
+ cd node
141
+ npm install
142
+ npm run build
143
+ npm test
144
+ ```
145
+
146
+ Or from the repo root: `mise run node-test`
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Embedded SpiceDB instance.
3
+ *
4
+ * A thin wrapper that starts SpiceDB via the C-shared library, connects over a Unix
5
+ * socket, and bootstraps schema and relationships via gRPC.
6
+ *
7
+ * Use permissions(), schema(), and watch() to access the full SpiceDB API.
8
+ */
9
+ import { v1 } from "@authzed/authzed-node";
10
+ import { type SpiceDBStartOptions } from "./ffi.js";
11
+ export declare class SpiceDBError extends Error {
12
+ constructor(message: string);
13
+ }
14
+ export declare class EmbeddedSpiceDB {
15
+ private readonly handle;
16
+ private readonly client;
17
+ private constructor();
18
+ /**
19
+ * Create a new embedded SpiceDB instance with a schema and relationships.
20
+ *
21
+ * @param schema - The SpiceDB schema definition (ZED language)
22
+ * @param relationships - Initial relationships (empty array allowed)
23
+ * @param options - Optional configuration (datastore, grpc_transport). Omit for defaults.
24
+ * @returns New EmbeddedSpiceDB instance
25
+ */
26
+ static create(schema: string, relationships?: v1.Relationship[], options?: SpiceDBStartOptions | null): Promise<EmbeddedSpiceDB>;
27
+ private bootstrap;
28
+ /**
29
+ * Permissions service client (CheckPermission, WriteRelationships, ReadRelationships, etc.)
30
+ */
31
+ permissions(): v1.ZedClientInterface;
32
+ /**
33
+ * Schema service client (ReadSchema, WriteSchema, ReflectSchema, etc.)
34
+ */
35
+ schema(): v1.ZedClientInterface;
36
+ /**
37
+ * Watch service client (watch for relationship changes)
38
+ */
39
+ watch(): v1.ZedClientInterface;
40
+ /**
41
+ * Dispose the instance and close the channel.
42
+ */
43
+ close(): void;
44
+ }
45
+ //# sourceMappingURL=embedded.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"embedded.d.ts","sourceRoot":"","sources":["../src/embedded.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,EAAE,EAAE,EAAE,MAAM,uBAAuB,CAAC;AAC3C,OAAO,EAGL,KAAK,mBAAmB,EACzB,MAAM,UAAU,CAAC;AAYlB,qBAAa,YAAa,SAAQ,KAAK;gBACzB,OAAO,EAAE,MAAM;CAI5B;AAED,qBAAa,eAAe;IAC1B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAwB;IAE/C,OAAO;IAKP;;;;;;;OAOG;WACU,MAAM,CACjB,MAAM,EAAE,MAAM,EACd,aAAa,GAAE,EAAE,CAAC,YAAY,EAAO,EACrC,OAAO,CAAC,EAAE,mBAAmB,GAAG,IAAI,GACnC,OAAO,CAAC,eAAe,CAAC;YAyBb,SAAS;IAqBvB;;OAEG;IACH,WAAW,IAAI,EAAE,CAAC,kBAAkB;IAIpC;;OAEG;IACH,MAAM,IAAI,EAAE,CAAC,kBAAkB;IAI/B;;OAEG;IACH,KAAK,IAAI,EAAE,CAAC,kBAAkB;IAI9B;;OAEG;IACH,KAAK,IAAI,IAAI;CAId"}
@@ -0,0 +1,94 @@
1
+ /**
2
+ * Embedded SpiceDB instance.
3
+ *
4
+ * A thin wrapper that starts SpiceDB via the C-shared library, connects over a Unix
5
+ * socket, and bootstraps schema and relationships via gRPC.
6
+ *
7
+ * Use permissions(), schema(), and watch() to access the full SpiceDB API.
8
+ */
9
+ import * as grpc from "@grpc/grpc-js";
10
+ import { v1 } from "@authzed/authzed-node";
11
+ import { spicedb_dispose, spicedb_start, } from "./ffi.js";
12
+ import { getTarget as getTcpTarget } from "./tcp_channel.js";
13
+ import { getTarget as getUnixSocketTarget } from "./unix_socket_channel.js";
14
+ const { NewClientWithChannelCredentials, RelationshipUpdate, RelationshipUpdate_Operation, WriteRelationshipsRequest, WriteSchemaRequest, } = v1;
15
+ export class SpiceDBError extends Error {
16
+ constructor(message) {
17
+ super(message);
18
+ this.name = "SpiceDBError";
19
+ }
20
+ }
21
+ export class EmbeddedSpiceDB {
22
+ handle;
23
+ client;
24
+ constructor(handle, client) {
25
+ this.handle = handle;
26
+ this.client = client;
27
+ }
28
+ /**
29
+ * Create a new embedded SpiceDB instance with a schema and relationships.
30
+ *
31
+ * @param schema - The SpiceDB schema definition (ZED language)
32
+ * @param relationships - Initial relationships (empty array allowed)
33
+ * @param options - Optional configuration (datastore, grpc_transport). Omit for defaults.
34
+ * @returns New EmbeddedSpiceDB instance
35
+ */
36
+ static async create(schema, relationships = [], options) {
37
+ const data = spicedb_start(options ?? undefined);
38
+ const { handle, grpc_transport, address } = data;
39
+ const target = grpc_transport === "unix"
40
+ ? getUnixSocketTarget(address)
41
+ : getTcpTarget(address);
42
+ const creds = grpc.credentials.createInsecure();
43
+ const client = NewClientWithChannelCredentials(target, creds);
44
+ const db = new EmbeddedSpiceDB(handle, client);
45
+ try {
46
+ await db.bootstrap(schema, relationships);
47
+ }
48
+ catch (e) {
49
+ db.close();
50
+ throw new SpiceDBError(`Failed to bootstrap: ${e instanceof Error ? e.message : String(e)}`);
51
+ }
52
+ return db;
53
+ }
54
+ async bootstrap(schema, relationships) {
55
+ const schemaRequest = WriteSchemaRequest.create({ schema });
56
+ await this.client.promises.writeSchema(schemaRequest);
57
+ if (relationships.length > 0) {
58
+ const updates = relationships.map((r) => RelationshipUpdate.create({
59
+ operation: RelationshipUpdate_Operation.TOUCH,
60
+ relationship: r,
61
+ }));
62
+ const writeRequest = WriteRelationshipsRequest.create({
63
+ updates,
64
+ });
65
+ await this.client.promises.writeRelationships(writeRequest);
66
+ }
67
+ }
68
+ /**
69
+ * Permissions service client (CheckPermission, WriteRelationships, ReadRelationships, etc.)
70
+ */
71
+ permissions() {
72
+ return this.client;
73
+ }
74
+ /**
75
+ * Schema service client (ReadSchema, WriteSchema, ReflectSchema, etc.)
76
+ */
77
+ schema() {
78
+ return this.client;
79
+ }
80
+ /**
81
+ * Watch service client (watch for relationship changes)
82
+ */
83
+ watch() {
84
+ return this.client;
85
+ }
86
+ /**
87
+ * Dispose the instance and close the channel.
88
+ */
89
+ close() {
90
+ this.client.close();
91
+ spicedb_dispose(this.handle);
92
+ }
93
+ }
94
+ //# sourceMappingURL=embedded.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"embedded.js","sourceRoot":"","sources":["../src/embedded.ts"],"names":[],"mappings":"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"}
package/dist/ffi.d.ts ADDED
@@ -0,0 +1,27 @@
1
+ /**
2
+ * FFI bindings to the SpiceDB C-shared library (shared/c).
3
+ */
4
+ export interface SpiceDBStartResult {
5
+ handle: number;
6
+ grpc_transport: "unix" | "tcp";
7
+ address: string;
8
+ }
9
+ export interface SpiceDBStartOptions {
10
+ /** Datastore: "memory" (default), "postgres", "cockroachdb", "spanner", "mysql" */
11
+ datastore?: string;
12
+ /** Connection string for remote datastores */
13
+ datastore_uri?: string;
14
+ /** gRPC transport: "unix" (default on Unix), "tcp" (default on Windows) */
15
+ grpc_transport?: string;
16
+ /** Path to Spanner service account JSON (Spanner only) */
17
+ spanner_credentials_file?: string;
18
+ /** Spanner emulator host (Spanner only) */
19
+ spanner_emulator_host?: string;
20
+ /** Prefix for all tables (MySQL only) */
21
+ mysql_table_prefix?: string;
22
+ /** Enable datastore Prometheus metrics (default: false; disabled allows multiple instances in same process) */
23
+ metrics_enabled?: boolean;
24
+ }
25
+ export declare function spicedb_start(options?: SpiceDBStartOptions | null): SpiceDBStartResult;
26
+ export declare function spicedb_dispose(handle: number): void;
27
+ //# sourceMappingURL=ffi.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ffi.d.ts","sourceRoot":"","sources":["../src/ffi.ts"],"names":[],"mappings":"AAAA;;GAEG;AAOH,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,cAAc,EAAE,MAAM,GAAG,KAAK,CAAC;IAC/B,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,mBAAmB;IAClC,mFAAmF;IACnF,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,8CAA8C;IAC9C,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,2EAA2E;IAC3E,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,0DAA0D;IAC1D,wBAAwB,CAAC,EAAE,MAAM,CAAC;IAClC,2CAA2C;IAC3C,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,yCAAyC;IACzC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,+GAA+G;IAC/G,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AA8ED,wBAAgB,aAAa,CAC3B,OAAO,CAAC,EAAE,mBAAmB,GAAG,IAAI,GACnC,kBAAkB,CAapB;AAED,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAOpD"}
package/dist/ffi.js ADDED
@@ -0,0 +1,82 @@
1
+ /**
2
+ * FFI bindings to the SpiceDB C-shared library (shared/c).
3
+ */
4
+ import koffi from "koffi";
5
+ import { accessSync } from "fs";
6
+ import { platform } from "os";
7
+ import { resolve } from "path";
8
+ function findLibrary() {
9
+ const currentPlatform = platform();
10
+ const libName = currentPlatform === "win32"
11
+ ? "spicedb.dll"
12
+ : currentPlatform === "darwin"
13
+ ? "libspicedb.dylib"
14
+ : "libspicedb.so";
15
+ const explicit = process.env.SPICEDB_LIBRARY_PATH;
16
+ if (explicit) {
17
+ const libExtensions = [".so", ".dylib", ".dll"];
18
+ const candidate = libExtensions.some((e) => explicit.endsWith(e))
19
+ ? explicit
20
+ : resolve(explicit, libName);
21
+ try {
22
+ accessSync(candidate);
23
+ return candidate;
24
+ }
25
+ catch (err) {
26
+ const message = err instanceof Error && err.message ? err.message : String(err);
27
+ throw new Error(`Unable to access SpiceDB library at '${candidate}' specified by SPICEDB_LIBRARY_PATH: ${message}`);
28
+ }
29
+ }
30
+ const searchPaths = [
31
+ "shared/c",
32
+ resolve(process.cwd(), "shared/c"),
33
+ resolve(process.cwd(), "../shared/c"),
34
+ resolve(process.cwd(), "node/../shared/c"),
35
+ ];
36
+ for (const base of searchPaths) {
37
+ const candidate = resolve(base, libName);
38
+ try {
39
+ accessSync(candidate);
40
+ return candidate;
41
+ }
42
+ catch {
43
+ // continue
44
+ }
45
+ }
46
+ return libName;
47
+ }
48
+ let lib = null;
49
+ function getLib() {
50
+ if (lib)
51
+ return lib;
52
+ const path = findLibrary();
53
+ const loaded = koffi.load(path);
54
+ const spicedb_free = loaded.func("void spicedb_free(char* ptr)");
55
+ koffi.disposable("HeapStr", "str", (ptr) => spicedb_free(ptr));
56
+ lib = {
57
+ spicedb_start: loaded.func("HeapStr spicedb_start(const char* optionsJson)"),
58
+ spicedb_dispose: loaded.func("HeapStr spicedb_dispose(unsigned long long handle)"),
59
+ };
60
+ return lib;
61
+ }
62
+ export function spicedb_start(options) {
63
+ const l = getLib();
64
+ const optionsJson = options != null ? JSON.stringify(options) : null;
65
+ const raw = l.spicedb_start(optionsJson);
66
+ if (!raw)
67
+ throw new Error("Null response from C library");
68
+ const data = JSON.parse(raw);
69
+ if (!data.success) {
70
+ throw new Error(data.error ?? "Unknown error");
71
+ }
72
+ return data.data;
73
+ }
74
+ export function spicedb_dispose(handle) {
75
+ const l = getLib();
76
+ const raw = l.spicedb_dispose(handle);
77
+ const data = JSON.parse(raw);
78
+ if (!data.success) {
79
+ throw new Error(data.error ?? "Unknown error");
80
+ }
81
+ }
82
+ //# sourceMappingURL=ffi.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ffi.js","sourceRoot":"","sources":["../src/ffi.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Embedded SpiceDB for TypeScript/Node.js — in-memory authorization server for tests and development.
3
+ */
4
+ export { EmbeddedSpiceDB, SpiceDBError } from "./embedded.js";
5
+ export type { SpiceDBStartOptions, SpiceDBStartResult } from "./ffi.js";
6
+ export { v1 } from "@authzed/authzed-node";
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC9D,YAAY,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAGxE,OAAO,EAAE,EAAE,EAAE,MAAM,uBAAuB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Embedded SpiceDB for TypeScript/Node.js — in-memory authorization server for tests and development.
3
+ */
4
+ export { EmbeddedSpiceDB, SpiceDBError } from "./embedded.js";
5
+ // Re-export authzed v1 for types and client creation
6
+ export { v1 } from "@authzed/authzed-node";
7
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAG9D,qDAAqD;AACrD,OAAO,EAAE,EAAE,EAAE,MAAM,uBAAuB,CAAC"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * gRPC target for TCP connections.
3
+ */
4
+ /** Returns the gRPC target string for a TCP connection (e.g. 127.0.0.1:50051). */
5
+ export declare function getTarget(address: string): string;
6
+ //# sourceMappingURL=tcp_channel.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tcp_channel.d.ts","sourceRoot":"","sources":["../src/tcp_channel.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,kFAAkF;AAClF,wBAAgB,SAAS,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAEjD"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * gRPC target for TCP connections.
3
+ */
4
+ /** Returns the gRPC target string for a TCP connection (e.g. 127.0.0.1:50051). */
5
+ export function getTarget(address) {
6
+ return address;
7
+ }
8
+ //# sourceMappingURL=tcp_channel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tcp_channel.js","sourceRoot":"","sources":["../src/tcp_channel.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,kFAAkF;AAClF,MAAM,UAAU,SAAS,CAAC,OAAe;IACvC,OAAO,OAAO,CAAC;AACjB,CAAC"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * gRPC target for Unix domain socket connections.
3
+ */
4
+ /** Returns the gRPC target string for a Unix domain socket (e.g. unix:///tmp/spicedb-123.sock). */
5
+ export declare function getTarget(address: string): string;
6
+ //# sourceMappingURL=unix_socket_channel.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"unix_socket_channel.d.ts","sourceRoot":"","sources":["../src/unix_socket_channel.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,mGAAmG;AACnG,wBAAgB,SAAS,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAEjD"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * gRPC target for Unix domain socket connections.
3
+ */
4
+ /** Returns the gRPC target string for a Unix domain socket (e.g. unix:///tmp/spicedb-123.sock). */
5
+ export function getTarget(address) {
6
+ return `unix://${address}`;
7
+ }
8
+ //# sourceMappingURL=unix_socket_channel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"unix_socket_channel.js","sourceRoot":"","sources":["../src/unix_socket_channel.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,mGAAmG;AACnG,MAAM,UAAU,SAAS,CAAC,OAAe;IACvC,OAAO,UAAU,OAAO,EAAE,CAAC;AAC7B,CAAC"}
package/package.json ADDED
@@ -0,0 +1,46 @@
1
+ {
2
+ "name": "spicedb-embedded",
3
+ "version": "0.1.0",
4
+ "description": "Embedded SpiceDB for TypeScript/Node.js — in-memory authorization using the standard gRPC API",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.js",
12
+ "default": "./dist/index.js"
13
+ }
14
+ },
15
+ "files": [
16
+ "dist"
17
+ ],
18
+ "scripts": {
19
+ "build": "tsc",
20
+ "prepublishOnly": "npm run build",
21
+ "test": "vitest run",
22
+ "format": "prettier --write .",
23
+ "format:check": "prettier --check .",
24
+ "lint": "npm run format:check"
25
+ },
26
+ "dependencies": {
27
+ "@authzed/authzed-node": "^1.5.0",
28
+ "@grpc/grpc-js": "^1.14.3",
29
+ "koffi": "^2.9.0"
30
+ },
31
+ "devDependencies": {
32
+ "@types/node": "^22.0.0",
33
+ "eslint": "^9.0.0",
34
+ "prettier": "^3.4.0",
35
+ "typescript": "^5.7.0",
36
+ "vitest": "^2.0.0"
37
+ },
38
+ "engines": {
39
+ "node": ">=18.18.0"
40
+ },
41
+ "license": "Apache-2.0",
42
+ "repository": {
43
+ "type": "git",
44
+ "url": "git+https://github.com/borkfork/spicedb-embedded.git"
45
+ }
46
+ }