buildless-cli 0.1.6

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.
Files changed (42) hide show
  1. package/README.md +49 -0
  2. package/dist/app.d.ts +9 -0
  3. package/dist/app.d.ts.map +1 -0
  4. package/dist/app.js +67 -0
  5. package/dist/app.js.map +1 -0
  6. package/dist/auth.d.ts +14 -0
  7. package/dist/auth.d.ts.map +1 -0
  8. package/dist/auth.js +100 -0
  9. package/dist/auth.js.map +1 -0
  10. package/dist/config.d.ts +14 -0
  11. package/dist/config.d.ts.map +1 -0
  12. package/dist/config.js +45 -0
  13. package/dist/config.js.map +1 -0
  14. package/dist/firestore.d.ts +28 -0
  15. package/dist/firestore.d.ts.map +1 -0
  16. package/dist/firestore.js +340 -0
  17. package/dist/firestore.js.map +1 -0
  18. package/dist/index.d.ts +3 -0
  19. package/dist/index.d.ts.map +1 -0
  20. package/dist/index.js +94 -0
  21. package/dist/index.js.map +1 -0
  22. package/dist/log.d.ts +3 -0
  23. package/dist/log.d.ts.map +1 -0
  24. package/dist/log.js +10 -0
  25. package/dist/log.js.map +1 -0
  26. package/dist/ui/app-search.d.ts +11 -0
  27. package/dist/ui/app-search.d.ts.map +1 -0
  28. package/dist/ui/app-search.js +83 -0
  29. package/dist/ui/app-search.js.map +1 -0
  30. package/dist/ui/runner.d.ts +12 -0
  31. package/dist/ui/runner.d.ts.map +1 -0
  32. package/dist/ui/runner.js +419 -0
  33. package/dist/ui/runner.js.map +1 -0
  34. package/dist/ui/useRunnerRenderSource.d.ts +53 -0
  35. package/dist/ui/useRunnerRenderSource.d.ts.map +1 -0
  36. package/dist/ui/useRunnerRenderSource.js +69 -0
  37. package/dist/ui/useRunnerRenderSource.js.map +1 -0
  38. package/dist/ui/useRunnerSession.d.ts +16 -0
  39. package/dist/ui/useRunnerSession.d.ts.map +1 -0
  40. package/dist/ui/useRunnerSession.js +188 -0
  41. package/dist/ui/useRunnerSession.js.map +1 -0
  42. package/package.json +41 -0
package/README.md ADDED
@@ -0,0 +1,49 @@
1
+ # buildless-cli
2
+
3
+ Terminal client for BuildLess/BLO published apps.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install -g buildless-cli
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ```bash
14
+ buildless
15
+ ```
16
+
17
+ The CLI opens a browser for login, then shows runnable apps:
18
+
19
+ - apps in your `projectAccess` list
20
+ - published store apps (fallback)
21
+
22
+ For each app, CLI resolves a version for the new runner backend:
23
+
24
+ - uses `publishedVersionId` when present
25
+ - otherwise falls back to the latest available project version
26
+
27
+ In runner:
28
+
29
+ - press `x` (or `q`) to return to app list
30
+ - press `Ctrl+C` to exit CLI
31
+
32
+ ## Configuration
33
+
34
+ Environment variables:
35
+
36
+ - `BLO_FIREBASE_PROJECT_ID` (required)
37
+ - `BLO_WEB_ORIGIN` (default: `http://localhost:3005`)
38
+ - `BLO_TERMINAL_URL` (default: `http://localhost:8080`)
39
+ - `BLO_CONFIG_PATH` (optional, overrides config path)
40
+
41
+ Config file is stored at:
42
+
43
+ - `~/.config/blo/cli.json` (Linux/macOS)
44
+
45
+ ## Development
46
+
47
+ ```bash
48
+ pnpm dev:cli
49
+ ```
package/dist/app.d.ts ADDED
@@ -0,0 +1,9 @@
1
+ import React from "react";
2
+ import type { CliConfig } from "./config.js";
3
+ export declare const App: React.FC<{
4
+ config: CliConfig;
5
+ idToken: string;
6
+ initialQuery?: string;
7
+ onExit: () => void;
8
+ }>;
9
+ //# sourceMappingURL=app.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../src/app.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA8B,MAAM,OAAO,CAAC;AAEnD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAM7C,eAAO,MAAM,GAAG,EAAE,KAAK,CAAC,EAAE,CAAC;IACzB,MAAM,EAAE,SAAS,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,MAAM,IAAI,CAAC;CACpB,CA6FA,CAAC"}
package/dist/app.js ADDED
@@ -0,0 +1,67 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useEffect, useState } from "react";
3
+ import { Box, Text } from "ink";
4
+ import { fetchRunnableAppsViaServer } from "./firestore.js";
5
+ import { AppSearch } from "./ui/app-search.js";
6
+ import { Runner } from "./ui/runner.js";
7
+ import { log } from "./log.js";
8
+ export const App = ({ config, idToken, initialQuery, onExit }) => {
9
+ const [apps, setApps] = useState([]);
10
+ const [loading, setLoading] = useState(true);
11
+ const [error, setError] = useState(null);
12
+ const [selectedApp, setSelectedApp] = useState(null);
13
+ useEffect(() => {
14
+ let mounted = true;
15
+ const load = async () => {
16
+ try {
17
+ setLoading(true);
18
+ log(`Loading apps from ${config.terminalUrl}`);
19
+ const results = await fetchRunnableAppsViaServer({
20
+ terminalUrl: config.terminalUrl,
21
+ idToken,
22
+ query: initialQuery,
23
+ });
24
+ if (!mounted)
25
+ return;
26
+ log(`Loaded ${results.length} apps`);
27
+ setApps(results);
28
+ setError(null);
29
+ }
30
+ catch (err) {
31
+ if (!mounted)
32
+ return;
33
+ setError(err instanceof Error ? err.message : String(err));
34
+ }
35
+ finally {
36
+ if (mounted)
37
+ setLoading(false);
38
+ }
39
+ };
40
+ void load();
41
+ return () => {
42
+ mounted = false;
43
+ };
44
+ }, [config.firebaseProjectId, config.terminalUrl, idToken, initialQuery]);
45
+ if (error) {
46
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { color: "red", children: error }), _jsx(Text, { dimColor: true, children: "Tip: Set BLO_DEBUG=1 for detailed logs." }), _jsx(Text, { children: "Press Ctrl+C to exit." })] }));
47
+ }
48
+ if (loading) {
49
+ return (_jsx(Box, { flexDirection: "column", children: _jsxs(Text, { color: "green", children: ["Loading apps from ", config.terminalUrl, "..."] }) }));
50
+ }
51
+ if (apps.length === 0) {
52
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { color: "yellow", children: "No apps found." }), _jsx(Text, { dimColor: true, children: initialQuery
53
+ ? `No apps matched "${initialQuery}". Try without a query filter.`
54
+ : "Your account has no projects and no published apps are available." }), _jsx(Text, { children: "Press Ctrl+C to exit." })] }));
55
+ }
56
+ if (!selectedApp) {
57
+ return (_jsx(AppSearch, { apps: apps, initialQuery: initialQuery, onSelect: (app) => {
58
+ log(`Selected app: ${app.projectId} (version=${app.versionId}, source=${app.versionSource})`);
59
+ setSelectedApp(app);
60
+ }, onExit: onExit }));
61
+ }
62
+ return (_jsx(Runner, { projectId: selectedApp.projectId, versionId: selectedApp.versionId, terminalUrl: config.terminalUrl, idToken: idToken, onBackToApps: () => {
63
+ log("Returning to app list");
64
+ setSelectedApp(null);
65
+ }, onExit: onExit }));
66
+ };
67
+ //# sourceMappingURL=app.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"app.js","sourceRoot":"","sources":["../src/app.tsx"],"names":[],"mappings":"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"}
package/dist/auth.d.ts ADDED
@@ -0,0 +1,14 @@
1
+ export type LoginResult = {
2
+ token: string;
3
+ exp: number | null;
4
+ appCheckToken?: string;
5
+ };
6
+ type LoginOptions = {
7
+ webOrigin: string;
8
+ };
9
+ export declare const decodeJwtPayload: (token: string) => Record<string, unknown> | null;
10
+ export declare const getTokenExp: (token: string) => number | null;
11
+ export declare const isTokenValid: (token?: string, exp?: number | null) => boolean;
12
+ export declare const startExternalLogin: (options: LoginOptions) => Promise<LoginResult>;
13
+ export {};
14
+ //# sourceMappingURL=auth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAMA,MAAM,MAAM,WAAW,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,aAAa,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAExF,KAAK,YAAY,GAAG;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,eAAO,MAAM,gBAAgB,GAAI,OAAO,MAAM,KAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAS1E,CAAC;AAEF,eAAO,MAAM,WAAW,GAAI,OAAO,MAAM,KAAG,MAAM,GAAG,IAIpD,CAAC;AAEF,eAAO,MAAM,YAAY,GAAI,QAAQ,MAAM,EAAE,MAAM,MAAM,GAAG,IAAI,KAAG,OAKlE,CAAC;AAIF,eAAO,MAAM,kBAAkB,GAAU,SAAS,YAAY,KAAG,OAAO,CAAC,WAAW,CAqEnF,CAAC"}
package/dist/auth.js ADDED
@@ -0,0 +1,100 @@
1
+ import { createServer } from "node:http";
2
+ import crypto from "node:crypto";
3
+ import { URL } from "node:url";
4
+ import open from "open";
5
+ import { log } from "./log.js";
6
+ export const decodeJwtPayload = (token) => {
7
+ const parts = token.split(".");
8
+ if (parts.length < 2)
9
+ return null;
10
+ try {
11
+ const payload = Buffer.from(parts[1], "base64url").toString("utf8");
12
+ return JSON.parse(payload);
13
+ }
14
+ catch {
15
+ return null;
16
+ }
17
+ };
18
+ export const getTokenExp = (token) => {
19
+ const payload = decodeJwtPayload(token);
20
+ if (!payload || typeof payload.exp !== "number")
21
+ return null;
22
+ return payload.exp;
23
+ };
24
+ export const isTokenValid = (token, exp) => {
25
+ if (!token)
26
+ return false;
27
+ if (exp == null)
28
+ return true;
29
+ const now = Math.floor(Date.now() / 1000);
30
+ return exp - 30 > now;
31
+ };
32
+ const LOGIN_TIMEOUT_MS = 120_000;
33
+ export const startExternalLogin = async (options) => {
34
+ const state = crypto.randomBytes(16).toString("hex");
35
+ const server = createServer();
36
+ const result = await new Promise((resolve, reject) => {
37
+ let settled = false;
38
+ const timeout = setTimeout(() => {
39
+ if (settled)
40
+ return;
41
+ settled = true;
42
+ server.close();
43
+ reject(new Error("Login timed out after 2 minutes. Please try again."));
44
+ }, LOGIN_TIMEOUT_MS);
45
+ server.on("request", (req, res) => {
46
+ if (!req.url)
47
+ return;
48
+ const url = new URL(req.url, "http://localhost");
49
+ if (url.pathname !== "/callback") {
50
+ res.writeHead(404, { "Content-Type": "text/plain" });
51
+ res.end("Not found");
52
+ return;
53
+ }
54
+ if (settled) {
55
+ res.writeHead(409, { "Content-Type": "text/plain" });
56
+ res.end("Login already handled. You can close this window.");
57
+ return;
58
+ }
59
+ const token = url.searchParams.get("token") || "";
60
+ const returnedState = url.searchParams.get("state") || "";
61
+ const appCheckToken = url.searchParams.get("appCheckToken") || undefined;
62
+ if (!token || returnedState !== state) {
63
+ settled = true;
64
+ res.writeHead(400, { "Content-Type": "text/plain" });
65
+ res.end("Invalid login response. You can close this window.");
66
+ clearTimeout(timeout);
67
+ log(`Login callback failed: token=${token ? "present" : "missing"}, state=${returnedState === state ? "match" : "mismatch"}`);
68
+ reject(new Error("Login failed: invalid callback. Make sure you completed the login in your browser."));
69
+ server.close();
70
+ return;
71
+ }
72
+ settled = true;
73
+ res.writeHead(200, { "Content-Type": "text/plain" });
74
+ res.end("Login complete. You can close this window.");
75
+ clearTimeout(timeout);
76
+ const exp = getTokenExp(token);
77
+ log(`Login callback received: exp=${exp}, appCheck=${appCheckToken ? "yes" : "no"}`);
78
+ resolve({ token, exp, appCheckToken });
79
+ server.close();
80
+ });
81
+ server.listen(0, "127.0.0.1", () => {
82
+ const address = server.address();
83
+ if (!address || typeof address === "string") {
84
+ clearTimeout(timeout);
85
+ reject(new Error("Failed to start local login server."));
86
+ return;
87
+ }
88
+ const callbackUrl = `http://127.0.0.1:${address.port}/callback`;
89
+ const loginUrl = new URL("/cli-auth", options.webOrigin);
90
+ loginUrl.searchParams.set("redirect_uri", callbackUrl);
91
+ loginUrl.searchParams.set("state", state);
92
+ log(`Login server listening on port ${address.port}`);
93
+ log(`Opening browser: ${loginUrl.toString()}`);
94
+ process.stderr.write("Opening browser for login...\n");
95
+ void open(loginUrl.toString());
96
+ });
97
+ });
98
+ return result;
99
+ };
100
+ //# sourceMappingURL=auth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.js","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ export type CliConfig = {
2
+ webOrigin: string;
3
+ terminalUrl: string;
4
+ firebaseProjectId: string;
5
+ idToken?: string;
6
+ tokenExp?: number;
7
+ appCheckToken?: string;
8
+ };
9
+ export declare const loadConfig: () => {
10
+ path: string;
11
+ config: CliConfig;
12
+ };
13
+ export declare const saveConfig: (configPath: string, config: CliConfig) => void;
14
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,SAAS,GAAG;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB,CAAC;AAcF,eAAO,MAAM,UAAU,QAAO;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,SAAS,CAAA;CA0B9D,CAAC;AAEF,eAAO,MAAM,UAAU,GAAI,YAAY,MAAM,EAAE,QAAQ,SAAS,KAAG,IAIlE,CAAC"}
package/dist/config.js ADDED
@@ -0,0 +1,45 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import os from "node:os";
4
+ const defaultWebOrigin = process.env.BLO_WEB_ORIGIN || "http://localhost:3005";
5
+ const defaultTerminalUrl = process.env.BLO_TERMINAL_URL || "http://localhost:8080";
6
+ const defaultProjectId = process.env.BLO_FIREBASE_PROJECT_ID || process.env.VITE_FIREBASE_PROJECT_ID || "";
7
+ const resolveConfigPath = () => {
8
+ if (process.env.BLO_CONFIG_PATH)
9
+ return process.env.BLO_CONFIG_PATH;
10
+ const home = os.homedir();
11
+ const configDir = process.env.XDG_CONFIG_HOME || path.join(home, ".config");
12
+ return path.join(configDir, "blo", "cli.json");
13
+ };
14
+ export const loadConfig = () => {
15
+ const configPath = resolveConfigPath();
16
+ const defaults = {
17
+ webOrigin: defaultWebOrigin,
18
+ terminalUrl: defaultTerminalUrl,
19
+ firebaseProjectId: defaultProjectId,
20
+ };
21
+ if (!fs.existsSync(configPath)) {
22
+ return { path: configPath, config: defaults };
23
+ }
24
+ try {
25
+ const raw = fs.readFileSync(configPath, "utf8");
26
+ const parsed = JSON.parse(raw);
27
+ return {
28
+ path: configPath,
29
+ config: {
30
+ ...defaults,
31
+ ...parsed,
32
+ },
33
+ };
34
+ }
35
+ catch (err) {
36
+ process.stderr.write(`Warning: failed to parse ${configPath}, using defaults: ${err instanceof Error ? err.message : String(err)}\n`);
37
+ return { path: configPath, config: defaults };
38
+ }
39
+ };
40
+ export const saveConfig = (configPath, config) => {
41
+ const dir = path.dirname(configPath);
42
+ fs.mkdirSync(dir, { recursive: true, mode: 0o700 });
43
+ fs.writeFileSync(configPath, JSON.stringify(config, null, 2), { mode: 0o600 });
44
+ };
45
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,MAAM,SAAS,CAAC;AAWzB,MAAM,gBAAgB,GAAG,OAAO,CAAC,GAAG,CAAC,cAAc,IAAI,uBAAuB,CAAC;AAC/E,MAAM,kBAAkB,GAAG,OAAO,CAAC,GAAG,CAAC,gBAAgB,IAAI,uBAAuB,CAAC;AACnF,MAAM,gBAAgB,GACpB,OAAO,CAAC,GAAG,CAAC,uBAAuB,IAAI,OAAO,CAAC,GAAG,CAAC,wBAAwB,IAAI,EAAE,CAAC;AAEpF,MAAM,iBAAiB,GAAG,GAAW,EAAE;IACrC,IAAI,OAAO,CAAC,GAAG,CAAC,eAAe;QAAE,OAAO,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC;IACpE,MAAM,IAAI,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC;IAC1B,MAAM,SAAS,GAAG,OAAO,CAAC,GAAG,CAAC,eAAe,IAAI,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;IAC5E,OAAO,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,KAAK,EAAE,UAAU,CAAC,CAAC;AACjD,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,UAAU,GAAG,GAAwC,EAAE;IAClE,MAAM,UAAU,GAAG,iBAAiB,EAAE,CAAC;IACvC,MAAM,QAAQ,GAAc;QAC1B,SAAS,EAAE,gBAAgB;QAC3B,WAAW,EAAE,kBAAkB;QAC/B,iBAAiB,EAAE,gBAAgB;KACpC,CAAC;IAEF,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAC/B,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC;IAChD,CAAC;IAED,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,EAAE,CAAC,YAAY,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;QAChD,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAuB,CAAC;QACrD,OAAO;YACL,IAAI,EAAE,UAAU;YAChB,MAAM,EAAE;gBACN,GAAG,QAAQ;gBACX,GAAG,MAAM;aACV;SACF,CAAC;IACJ,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,4BAA4B,UAAU,qBAAqB,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACtI,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC;IAChD,CAAC;AACH,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,UAAkB,EAAE,MAAiB,EAAQ,EAAE;IACxE,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;IACrC,EAAE,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IACpD,EAAE,CAAC,aAAa,CAAC,UAAU,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;AACjF,CAAC,CAAC"}
@@ -0,0 +1,28 @@
1
+ export type RunnableApp = {
2
+ id: string;
3
+ projectId: string;
4
+ versionId: string;
5
+ versionSource: "published" | "latest" | "store";
6
+ publishedVersionId?: string;
7
+ name?: string;
8
+ displayName?: string;
9
+ tagline?: string;
10
+ description?: string;
11
+ accessTier?: string;
12
+ authorName?: string;
13
+ ownerDisplayName?: string;
14
+ appSource?: "user" | "published";
15
+ };
16
+ export declare const fetchRunnableAppsViaServer: (opts: {
17
+ terminalUrl: string;
18
+ idToken: string;
19
+ query?: string;
20
+ }) => Promise<RunnableApp[]>;
21
+ export declare const fetchRunnableApps: (opts: {
22
+ projectId: string;
23
+ idToken: string;
24
+ appCheckToken?: string;
25
+ query?: string;
26
+ }) => Promise<RunnableApp[]>;
27
+ export declare const filterApp: (app: RunnableApp, term: string) => boolean;
28
+ //# sourceMappingURL=firestore.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"firestore.d.ts","sourceRoot":"","sources":["../src/firestore.ts"],"names":[],"mappings":"AAGA,MAAM,MAAM,WAAW,GAAG;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,WAAW,GAAG,QAAQ,GAAG,OAAO,CAAC;IAChD,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,SAAS,CAAC,EAAE,MAAM,GAAG,WAAW,CAAC;CAClC,CAAC;AA4TF,eAAO,MAAM,0BAA0B,GAAU,MAAM;IACrD,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,KAAG,OAAO,CAAC,WAAW,EAAE,CAoCxB,CAAC;AAEF,eAAO,MAAM,iBAAiB,GAAU,MAAM;IAC5C,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,KAAG,OAAO,CAAC,WAAW,EAAE,CA+BxB,CAAC;AAEF,eAAO,MAAM,SAAS,GAAI,KAAK,WAAW,EAAE,MAAM,MAAM,KAAG,OAaK,CAAC"}