@knpkv/alchemy-console 0.1.0 → 0.2.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.
Files changed (51) hide show
  1. package/README.md +71 -4
  2. package/dist/api.d.ts +126 -0
  3. package/dist/api.d.ts.map +1 -0
  4. package/dist/api.js +62 -0
  5. package/dist/api.js.map +1 -0
  6. package/dist/client/bootstrap.js +2834 -0
  7. package/dist/main.d.ts +3 -0
  8. package/dist/main.d.ts.map +1 -0
  9. package/dist/main.js +39 -0
  10. package/dist/main.js.map +1 -0
  11. package/dist/s3-live.d.ts +7 -2
  12. package/dist/s3-live.d.ts.map +1 -1
  13. package/dist/s3-live.js +3 -2
  14. package/dist/s3-live.js.map +1 -1
  15. package/dist/server/Bootstrap.d.ts +4 -0
  16. package/dist/server/Bootstrap.d.ts.map +1 -0
  17. package/dist/server/Bootstrap.js +12 -0
  18. package/dist/server/Bootstrap.js.map +1 -0
  19. package/dist/server/Catalog.d.ts +27 -0
  20. package/dist/server/Catalog.d.ts.map +1 -0
  21. package/dist/server/Catalog.js +83 -0
  22. package/dist/server/Catalog.js.map +1 -0
  23. package/dist/server/Config.d.ts +17 -0
  24. package/dist/server/Config.d.ts.map +1 -0
  25. package/dist/server/Config.js +23 -0
  26. package/dist/server/Config.js.map +1 -0
  27. package/dist/server/ControlSocket.d.ts +19 -0
  28. package/dist/server/ControlSocket.d.ts.map +1 -0
  29. package/dist/server/ControlSocket.js +147 -0
  30. package/dist/server/ControlSocket.js.map +1 -0
  31. package/dist/server/HttpApplication.d.ts +7 -0
  32. package/dist/server/HttpApplication.d.ts.map +1 -0
  33. package/dist/server/HttpApplication.js +46 -0
  34. package/dist/server/HttpApplication.js.map +1 -0
  35. package/dist/server/Launch.d.ts +7 -0
  36. package/dist/server/Launch.d.ts.map +1 -0
  37. package/dist/server/Launch.js +17 -0
  38. package/dist/server/Launch.js.map +1 -0
  39. package/dist/server/OwnerSession.d.ts +9 -0
  40. package/dist/server/OwnerSession.d.ts.map +1 -0
  41. package/dist/server/OwnerSession.js +27 -0
  42. package/dist/server/OwnerSession.js.map +1 -0
  43. package/dist/server/Server.d.ts +6 -0
  44. package/dist/server/Server.d.ts.map +1 -0
  45. package/dist/server/Server.js +22 -0
  46. package/dist/server/Server.js.map +1 -0
  47. package/dist/server/Sources.d.ts +12 -0
  48. package/dist/server/Sources.d.ts.map +1 -0
  49. package/dist/server/Sources.js +118 -0
  50. package/dist/server/Sources.js.map +1 -0
  51. package/package.json +17 -6
@@ -0,0 +1,7 @@
1
+ import { type BrowserLaunch, BrowserLaunchOutputError } from "@knpkv/browser-pairing/launch";
2
+ import { Effect, Stdio } from "effect";
3
+ import { Catalog } from "./Catalog.js";
4
+ import { type ControlOptions } from "./ControlSocket.js";
5
+ /** The launch helper owns printing/opening; the catalog and server lifetime stay with this foreground process. */
6
+ export declare const runConsole: (options: ControlOptions, launch: BrowserLaunch, printOnly: boolean) => Effect.Effect<void, import("effect/PlatformError").BadArgument | BrowserLaunchOutputError | import("@knpkv/browser-pairing").BrowserPairingError | import("./ControlSocket.js").ControlError | import("@knpkv/browser-pairing").CredentialCookieError | import("effect/PlatformError").PlatformError | import("effect/http/HttpServerError").ServeError | import("@knpkv/browser-pairing/owner-session").UnsafeLoopbackAddressError, import("@knpkv/browser-pairing/launch").BrowserOpener | Catalog | import("effect/Crypto").Crypto | import("effect/FileSystem").FileSystem | import("effect/Path").Path | Stdio.Stdio>;
7
+ //# sourceMappingURL=Launch.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Launch.d.ts","sourceRoot":"","sources":["../../src/server/Launch.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,aAAa,EAAE,wBAAwB,EAAe,MAAM,+BAA+B,CAAA;AAEzG,OAAO,EAAE,MAAM,EAAS,KAAK,EAAU,MAAM,QAAQ,CAAA;AACrD,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AACtC,OAAO,EAAE,KAAK,cAAc,EAAmB,MAAM,oBAAoB,CAAA;AAGzE,kHAAkH;AAClH,eAAO,MAAM,UAAU,oqBAqBrB,CAAA"}
@@ -0,0 +1,17 @@
1
+ import { BrowserLaunchOutputError, openOrServe } from "@knpkv/browser-pairing/launch";
2
+ import { serveWithBootstrapUrl } from "@knpkv/browser-pairing/owner-session";
3
+ import { Effect, Layer, Stdio, Stream } from "effect";
4
+ import { Catalog } from "./Catalog.js";
5
+ import { requestLoginUrl } from "./ControlSocket.js";
6
+ import { makeServer } from "./Server.js";
7
+ /** The launch helper owns printing/opening; the catalog and server lifetime stay with this foreground process. */
8
+ export const runConsole = Effect.fn("AlchemyConsole.runConsole")(function* (options, launch, printOnly) {
9
+ const catalog = yield* Catalog;
10
+ const serve = (onReady) => serveWithBootstrapUrl((ready) => makeServer(options, ready).pipe(Layer.provide(Layer.succeed(Catalog, catalog))), onReady);
11
+ if (printOnly) {
12
+ const stdio = yield* Stdio.Stdio;
13
+ return yield* serve((url) => Stream.make(`${url}\n`).pipe(Stream.run(stdio.stdout({ endOnDone: false })), Effect.mapError(() => new BrowserLaunchOutputError({}))));
14
+ }
15
+ return yield* openOrServe({ login: requestLoginUrl(options), serve, launch });
16
+ });
17
+ //# sourceMappingURL=Launch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Launch.js","sourceRoot":"","sources":["../../src/server/Launch.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsB,wBAAwB,EAAE,WAAW,EAAE,MAAM,+BAA+B,CAAA;AACzG,OAAO,EAAE,qBAAqB,EAAE,MAAM,sCAAsC,CAAA;AAC5E,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AACrD,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AACtC,OAAO,EAAuB,eAAe,EAAE,MAAM,oBAAoB,CAAA;AACzE,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAExC,kHAAkH;AAClH,MAAM,CAAC,MAAM,UAAU,GAAG,MAAM,CAAC,EAAE,CAAC,2BAA2B,CAAC,CAAC,QAAQ,CAAC,EACxE,OAAuB,EACvB,MAAqB,EACrB,SAAkB;IAElB,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,OAAO,CAAA;IAC9B,MAAM,KAAK,GAAG,CAAC,OAAuE,EAAE,EAAE,CACxF,qBAAqB,CACnB,CAAC,KAAK,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,EAC1F,OAAO,CACR,CAAA;IACH,IAAI,SAAS,EAAE,CAAC;QACd,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,KAAK,CAAC,KAAK,CAAA;QAChC,OAAO,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE,CAC1B,MAAM,CAAC,IAAI,CAAC,GAAG,GAAG,IAAI,CAAC,CAAC,IAAI,CAC1B,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC,CAAC,EAC9C,MAAM,CAAC,QAAQ,CAAC,GAAG,EAAE,CAAC,IAAI,wBAAwB,CAAC,EAAE,CAAC,CAAC,CACxD,CACF,CAAA;IACH,CAAC;IACD,OAAO,KAAK,CAAC,CAAC,WAAW,CAAC,EAAE,KAAK,EAAE,eAAe,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAA;AAC/E,CAAC,CAAC,CAAA"}
@@ -0,0 +1,9 @@
1
+ import * as OwnerSession from "@knpkv/browser-pairing/owner-session";
2
+ import { Effect, Layer } from "effect";
3
+ import { OwnerAuth } from "../api.js";
4
+ /** Fresh process session. Codes are minted only after the authenticated HTTP listener has built. */
5
+ export declare const makeOwnerSession: (port: number) => Effect.Effect<OwnerSession.OwnerSessionService, import("@knpkv/browser-pairing").BrowserPairingError | import("@knpkv/browser-pairing").CredentialCookieError | OwnerSession.UnsafeLoopbackAddressError, import("effect/Crypto").Crypto>;
6
+ /** Credential-bearing URL. Stdout and browser launch are its only announcement boundaries. */
7
+ export declare const mintBootstrapUrl: (session: OwnerSession.OwnerSessionService) => Effect.Effect<string, import("@knpkv/browser-pairing").BrowserPairingError, never>;
8
+ export declare const ownerAuthLayer: Layer.Layer<OwnerAuth, never, OwnerSession.OwnerSession>;
9
+ //# sourceMappingURL=OwnerSession.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"OwnerSession.d.ts","sourceRoot":"","sources":["../../src/server/OwnerSession.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,YAAY,MAAM,sCAAsC,CAAA;AACpE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAA;AAEtC,OAAO,EAAa,SAAS,EAAgB,MAAM,WAAW,CAAA;AAE9D,oGAAoG;AACpG,eAAO,MAAM,gBAAgB,SAAU,MAAM,6OAQzC,CAAA;AAEJ,8FAA8F;AAC9F,eAAO,MAAM,gBAAgB,YAAa,YAAY,CAAC,mBAAmB,uFAGvE,CAAA;AAEH,eAAO,MAAM,cAAc,0DAe1B,CAAA"}
@@ -0,0 +1,27 @@
1
+ import * as OwnerSession from "@knpkv/browser-pairing/owner-session";
2
+ import { Effect, Layer } from "effect";
3
+ import { HttpServerRequest } from "effect/http";
4
+ import { Forbidden, OwnerAuth, Unauthorized } from "../api.js";
5
+ /** Fresh process session. Codes are minted only after the authenticated HTTP listener has built. */
6
+ export const makeOwnerSession = (port) => OwnerSession.make({
7
+ product: "alchemy-console",
8
+ cookieName: `alchemy_console_owner_${port}`,
9
+ writes: "none",
10
+ freshUrlHint: "run `alchemy-console login` for a fresh URL",
11
+ authorityOrigin: OwnerSession.loopbackOrigin("127.0.0.1", port),
12
+ browserOrigin: OwnerSession.loopbackOrigin("127.0.0.1", port)
13
+ });
14
+ /** Credential-bearing URL. Stdout and browser launch are its only announcement boundaries. */
15
+ export const mintBootstrapUrl = (session) => session.mintBootstrapCode.pipe(Effect.map((code) => OwnerSession.bootstrapUrl(session.browserOrigin, code)));
16
+ export const ownerAuthLayer = Layer.effect(OwnerAuth, Effect.gen(function* () {
17
+ const session = yield* OwnerSession.OwnerSession;
18
+ return OwnerAuth.of(Effect.fn("AlchemyConsole.authorizeOwner")(function* (httpEffect) {
19
+ const request = yield* HttpServerRequest.HttpServerRequest;
20
+ yield* session.authorizeHttp(request.cookies[session.cookieName] ?? "").pipe(Effect.catchTags({
21
+ OwnerSessionUnauthorizedError: () => Effect.fail(new Unauthorized({})),
22
+ OwnerSessionForbiddenError: () => Effect.fail(new Forbidden({}))
23
+ }));
24
+ return yield* httpEffect;
25
+ }));
26
+ }));
27
+ //# sourceMappingURL=OwnerSession.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"OwnerSession.js","sourceRoot":"","sources":["../../src/server/OwnerSession.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,YAAY,MAAM,sCAAsC,CAAA;AACpE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAA;AACtC,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAA;AAC/C,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,WAAW,CAAA;AAE9D,oGAAoG;AACpG,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,IAAY,EAAE,EAAE,CAC/C,YAAY,CAAC,IAAI,CAAC;IAChB,OAAO,EAAE,iBAAiB;IAC1B,UAAU,EAAE,yBAAyB,IAAI,EAAE;IAC3C,MAAM,EAAE,MAAM;IACd,YAAY,EAAE,6CAA6C;IAC3D,eAAe,EAAE,YAAY,CAAC,cAAc,CAAC,WAAW,EAAE,IAAI,CAAC;IAC/D,aAAa,EAAE,YAAY,CAAC,cAAc,CAAC,WAAW,EAAE,IAAI,CAAC;CAC9D,CAAC,CAAA;AAEJ,8FAA8F;AAC9F,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,OAAyC,EAAE,EAAE,CAC5E,OAAO,CAAC,iBAAiB,CAAC,IAAI,CAC5B,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,YAAY,CAAC,YAAY,CAAC,OAAO,CAAC,aAAa,EAAE,IAAI,CAAC,CAAC,CAC7E,CAAA;AAEH,MAAM,CAAC,MAAM,cAAc,GAAG,KAAK,CAAC,MAAM,CACxC,SAAS,EACT,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;IAClB,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,YAAY,CAAC,YAAY,CAAA;IAChD,OAAO,SAAS,CAAC,EAAE,CACjB,MAAM,CAAC,EAAE,CAAC,+BAA+B,CAAC,CAAC,QAAQ,CAAC,EAAC,UAAU;QAC7D,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,iBAAiB,CAAC,iBAAiB,CAAA;QAC1D,KAAK,CAAC,CAAC,OAAO,CAAC,aAAa,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC;YAC5F,6BAA6B,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,YAAY,CAAC,EAAE,CAAC,CAAC;YACtE,0BAA0B,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,SAAS,CAAC,EAAE,CAAC,CAAC;SACjE,CAAC,CAAC,CAAA;QACH,OAAO,KAAK,CAAC,CAAC,UAAU,CAAA;IAC1B,CAAC,CAAC,CACH,CAAA;AACH,CAAC,CAAC,CACH,CAAA"}
@@ -0,0 +1,6 @@
1
+ import * as OwnerSession from "@knpkv/browser-pairing/owner-session";
2
+ import { Deferred, Layer } from "effect";
3
+ import { type ControlOptions } from "./ControlSocket.js";
4
+ /** Catalog and platform services are supplied by the executable. This layer owns both listeners. */
5
+ export declare const makeServer: (options: ControlOptions, ready: Deferred.Deferred<string>) => Layer.Layer<never, import("effect/PlatformError").BadArgument | import("@knpkv/browser-pairing").BrowserPairingError | import("./ControlSocket.js").ControlError | import("@knpkv/browser-pairing").CredentialCookieError | import("effect/PlatformError").PlatformError | import("effect/http/HttpServerError").ServeError | OwnerSession.UnsafeLoopbackAddressError, import("./Catalog.ts").Catalog | import("effect/Crypto").Crypto | import("effect/FileSystem").FileSystem | import("effect/Path").Path>;
6
+ //# sourceMappingURL=Server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Server.d.ts","sourceRoot":"","sources":["../../src/server/Server.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,YAAY,MAAM,sCAAsC,CAAA;AACpE,OAAO,EAAE,QAAQ,EAAU,KAAK,EAAE,MAAM,QAAQ,CAAA;AAGhD,OAAO,EAAE,KAAK,cAAc,EAAiB,MAAM,oBAAoB,CAAA;AAIvE,oGAAoG;AACpG,eAAO,MAAM,UAAU,YAAa,cAAc,SAAS,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC,kfAuBwB,CAAA"}
@@ -0,0 +1,22 @@
1
+ import { NodeHttpServer } from "@effect/platform-node";
2
+ import * as OwnerSession from "@knpkv/browser-pairing/owner-session";
3
+ import { Deferred, Effect, Layer } from "effect";
4
+ import { Etag, HttpPlatform, HttpRouter, HttpServer } from "effect/http";
5
+ import { createServer } from "node:http";
6
+ import { controlSocket } from "./ControlSocket.js";
7
+ import { application } from "./HttpApplication.js";
8
+ import { makeOwnerSession, mintBootstrapUrl } from "./OwnerSession.js";
9
+ /** Catalog and platform services are supplied by the executable. This layer owns both listeners. */
10
+ export const makeServer = (options, ready) => Layer.unwrap(Effect.gen(function* () {
11
+ const server = yield* HttpServer.HttpServer;
12
+ if (server.address._tag === "UnixPathAddress")
13
+ return yield* Effect.die("Alchemy console requires a TCP listener");
14
+ const port = server.address.port;
15
+ const session = yield* makeOwnerSession(port);
16
+ const listening = yield* Deferred.make();
17
+ const control = Layer.effectDiscard(controlSocket({ ...options, port }, session, Deferred.await(listening)));
18
+ return HttpRouter.serve(application, { disableLogger: true, disableListenLog: true }).pipe(Layer.provide(Layer.succeed(OwnerSession.OwnerSession, session)), Layer.provide(HttpPlatform.layer), Layer.provide(Etag.layer),
19
+ // The control socket can mint only after the HTTP routes are built and listening.
20
+ Layer.tap(() => Layer.build(control)), Layer.tap(() => mintBootstrapUrl(session).pipe(Effect.tap(() => Deferred.succeed(listening, undefined)), Effect.flatMap((url) => Deferred.succeed(ready, url)))));
21
+ })).pipe(Layer.provide(NodeHttpServer.layerServer(createServer, { host: "127.0.0.1", port: options.port })));
22
+ //# sourceMappingURL=Server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Server.js","sourceRoot":"","sources":["../../src/server/Server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAA;AACtD,OAAO,KAAK,YAAY,MAAM,sCAAsC,CAAA;AACpE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAA;AAChD,OAAO,EAAE,IAAI,EAAE,YAAY,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AACxE,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAA;AACxC,OAAO,EAAuB,aAAa,EAAE,MAAM,oBAAoB,CAAA;AACvE,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAA;AAClD,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAA;AAEtE,oGAAoG;AACpG,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,OAAuB,EAAE,KAAgC,EAAE,EAAE,CACtF,KAAK,CAAC,MAAM,CACV,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;IAClB,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,UAAU,CAAC,UAAU,CAAA;IAC3C,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,KAAK,iBAAiB;QAAE,OAAO,KAAK,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,yCAAyC,CAAC,CAAA;IAClH,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO,CAAC,IAAI,CAAA;IAChC,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAA;IAC7C,MAAM,SAAS,GAAG,KAAK,CAAC,CAAC,QAAQ,CAAC,IAAI,EAAQ,CAAA;IAC9C,MAAM,OAAO,GAAG,KAAK,CAAC,aAAa,CAAC,aAAa,CAAC,EAAE,GAAG,OAAO,EAAE,IAAI,EAAE,EAAE,OAAO,EAAE,QAAQ,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,CAAA;IAC5G,OAAO,UAAU,CAAC,KAAK,CAAC,WAAW,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,gBAAgB,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CACxF,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,YAAY,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC,EAChE,KAAK,CAAC,OAAO,CAAC,YAAY,CAAC,KAAK,CAAC,EACjC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC;IACzB,kFAAkF;IAClF,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,EACrC,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,CACb,gBAAgB,CAAC,OAAO,CAAC,CAAC,IAAI,CAC5B,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,OAAO,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC,EACxD,MAAM,CAAC,OAAO,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,QAAQ,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,CACtD,CACF,CACF,CAAA;AACH,CAAC,CAAC,CACH,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,cAAc,CAAC,WAAW,CAAC,YAAY,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAA"}
@@ -0,0 +1,12 @@
1
+ import { FileSystem, Layer, Path } from "effect";
2
+ import { HttpClient } from "effect/http";
3
+ import { SsoCredentials } from "../sso-credentials.js";
4
+ import { StackSource } from "./Catalog.js";
5
+ /** Runtime configuration contains server-private paths. It is never an API response. */
6
+ export interface SourceOptions {
7
+ readonly root: string;
8
+ readonly awsConfigFile: string;
9
+ }
10
+ /** Reads only when the authenticated catalog is requested; each project/profile/bucket reports its own failure. */
11
+ export declare const sourceLayer: (options: SourceOptions) => Layer.Layer<StackSource, never, FileSystem.FileSystem | HttpClient.HttpClient | Path.Path | SsoCredentials>;
12
+ //# sourceMappingURL=Sources.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Sources.d.ts","sourceRoot":"","sources":["../../src/server/Sources.ts"],"names":[],"mappings":"AAAA,OAAO,EAAU,UAAU,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAA;AACxD,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAQxC,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAA;AAEtD,OAAO,EAAwB,WAAW,EAAE,MAAM,cAAc,CAAA;AAEhE,wFAAwF;AACxF,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAA;CAC/B;AAED,mHAAmH;AACnH,eAAO,MAAM,WAAW,YAAa,aAAa,gHAgH/C,CAAA"}
@@ -0,0 +1,118 @@
1
+ import { Effect, FileSystem, Layer, Path } from "effect";
2
+ import { HttpClient } from "effect/http";
3
+ import { CatalogReadError } from "../api.js";
4
+ import { discoverProjects } from "../discovery.js";
5
+ import { readLocalState } from "../local-state.js";
6
+ import { discoverSsoProfiles } from "../profiles.js";
7
+ import { makeS3Reader } from "../s3-live.js";
8
+ import { S3Reader } from "../s3-reader.js";
9
+ import { discoverS3Buckets, discoverS3Stacks, readS3State } from "../s3-state.js";
10
+ import { SsoCredentials } from "../sso-credentials.js";
11
+ import { readAlchemyVersion } from "../version.js";
12
+ import { StackSource } from "./Catalog.js";
13
+ /** Reads only when the authenticated catalog is requested; each project/profile/bucket reports its own failure. */
14
+ export const sourceLayer = (options) => Layer.effect(StackSource, Effect.gen(function* () {
15
+ const fs = yield* FileSystem.FileSystem;
16
+ const path = yield* Path.Path;
17
+ // Capture the source adapter's services once; read closures need no transport/request context.
18
+ const client = yield* HttpClient.HttpClient;
19
+ const sso = yield* SsoCredentials;
20
+ const local = (effect) => effect.pipe(Effect.provideService(FileSystem.FileSystem, fs), Effect.provideService(Path.Path, path));
21
+ const discover = Effect.fn("AlchemyConsole.discoverSources")(function* () {
22
+ const stacks = [];
23
+ const issues = [];
24
+ const projects = yield* Effect.result(local(discoverProjects(options.root)));
25
+ if (projects._tag === "Failure")
26
+ issues.push({ source: "local", reason: projects.failure.reason });
27
+ else {
28
+ if (projects.success.length > 1000)
29
+ return yield* new CatalogReadError({ reason: "limit" });
30
+ for (const project of projects.success) {
31
+ const found = yield* Effect.result(local(Effect.gen(function* () {
32
+ const version = yield* readAlchemyVersion(project.directory, options.root);
33
+ // A source with no resource state need not use one of the supported local encodings.
34
+ if (!(yield* fs.exists(path.join(project.directory, ".alchemy", "state"))))
35
+ return [];
36
+ const state = yield* readLocalState(project.directory, version);
37
+ return state.stacks.map((stack) => ({
38
+ key: JSON.stringify(["local", project.directory, stack.app, stack.stage]),
39
+ summary: {
40
+ app: stack.app,
41
+ stage: stack.stage,
42
+ backend: "local",
43
+ alchemyVersion: version,
44
+ lastDeploy: stack.lastDeploy,
45
+ resourceCount: stack.resources.length
46
+ },
47
+ read: local(readLocalState(project.directory, version)).pipe(Effect.mapError((error) => new CatalogReadError({ reason: error.reason })), Effect.flatMap((fresh) => {
48
+ const current = fresh.stacks.find((item) => item.app === stack.app && item.stage === stack.stage);
49
+ return current === undefined ?
50
+ Effect.fail(new CatalogReadError({ reason: "not-found" })) :
51
+ Effect.succeed(current);
52
+ }))
53
+ }));
54
+ })).pipe(Effect.mapError((error) => new CatalogReadError({
55
+ reason: error._tag === "PlatformError" ? "io" : error.reason
56
+ }))));
57
+ if (found._tag === "Failure")
58
+ issues.push({ source: "local", reason: found.failure.reason });
59
+ else
60
+ for (const stack of found.success)
61
+ stacks.push(stack);
62
+ if (stacks.length > 1000 || issues.length > 1000)
63
+ return yield* new CatalogReadError({ reason: "limit" });
64
+ }
65
+ }
66
+ const profiles = yield* Effect.result(local(discoverSsoProfiles(options.awsConfigFile)));
67
+ if (profiles._tag === "Failure")
68
+ issues.push({ source: "s3", reason: profiles.failure.reason });
69
+ else {
70
+ if (profiles.success.length > 100)
71
+ return yield* new CatalogReadError({ reason: "limit" });
72
+ for (const profile of profiles.success) {
73
+ const reader = yield* makeS3Reader(profile).pipe(Effect.provideService(HttpClient.HttpClient, client), Effect.provideService(SsoCredentials, sso));
74
+ const buckets = yield* Effect.result(discoverS3Buckets().pipe(Effect.provideService(S3Reader, reader)));
75
+ if (buckets._tag === "Failure") {
76
+ issues.push({ source: "s3", reason: buckets.failure.reason });
77
+ continue;
78
+ }
79
+ if (buckets.success.length > 1000)
80
+ return yield* new CatalogReadError({ reason: "limit" });
81
+ for (const bucket of buckets.success) {
82
+ const found = yield* Effect.result(discoverS3Stacks(bucket).pipe(Effect.provideService(S3Reader, reader)));
83
+ if (found._tag === "Failure")
84
+ issues.push({ source: "s3", reason: found.failure.reason });
85
+ else {
86
+ for (const stack of found.success) {
87
+ stacks.push({
88
+ key: JSON.stringify([
89
+ "s3",
90
+ profile.name,
91
+ profile.account,
92
+ bucket.name,
93
+ stack.prefix,
94
+ stack.app,
95
+ stack.stage
96
+ ]),
97
+ summary: {
98
+ app: stack.app,
99
+ stage: stack.stage,
100
+ backend: "s3",
101
+ alchemyVersion: null,
102
+ lastDeploy: null,
103
+ resourceCount: null
104
+ },
105
+ read: readS3State(stack).pipe(Effect.provideService(S3Reader, reader), Effect.mapError((error) => new CatalogReadError({ reason: error.reason })))
106
+ });
107
+ }
108
+ }
109
+ if (stacks.length > 1000 || issues.length > 1000)
110
+ return yield* new CatalogReadError({ reason: "limit" });
111
+ }
112
+ }
113
+ }
114
+ return { stacks, issues };
115
+ });
116
+ return StackSource.of({ discover: discover() });
117
+ }));
118
+ //# sourceMappingURL=Sources.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Sources.js","sourceRoot":"","sources":["../../src/server/Sources.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@knpkv/alchemy-console",
3
- "version": "0.1.0",
3
+ "version": "0.2.1",
4
4
  "description": "Read-only discovery and state readers for local Alchemy stacks",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -12,10 +12,17 @@
12
12
  "sideEffects": false,
13
13
  "main": "./dist/index.js",
14
14
  "types": "./dist/index.d.ts",
15
+ "bin": {
16
+ "alchemy-console": "./dist/main.js"
17
+ },
15
18
  "exports": {
16
19
  ".": {
17
20
  "types": "./dist/index.d.ts",
18
21
  "import": "./dist/index.js"
22
+ },
23
+ "./api": {
24
+ "types": "./dist/api.d.ts",
25
+ "import": "./dist/api.js"
19
26
  }
20
27
  },
21
28
  "files": [
@@ -29,24 +36,28 @@
29
36
  "effect": "4.0.0"
30
37
  },
31
38
  "devDependencies": {
32
- "@effect/platform-node": "4.0.0",
33
39
  "@effect/vitest": "4.0.0",
40
+ "@playwright/test": "1.63.0",
34
41
  "@types/node": "^26.6.4",
35
42
  "effect": "4.0.0",
36
43
  "typescript": "npm:@typescript/typescript6@^6.0.2",
37
- "vitest": "^5.0.3"
44
+ "vitest": "^5.0.3",
45
+ "vite": "^8.3.2"
38
46
  },
39
47
  "dependencies": {
40
48
  "@aws-sdk/credential-provider-sso": "^3.973.16",
49
+ "@effect/platform-node": "4.0.0",
41
50
  "@distilled.cloud/aws": "^1.0.0-rc.13",
42
51
  "jsonc-parser": "^3.3.1",
43
52
  "yaml": "^2.9.1",
44
- "@knpkv/bounded-io": "^0.2.0"
53
+ "@knpkv/bounded-io": "^0.3.0",
54
+ "@knpkv/browser-pairing": "^0.5.1"
45
55
  },
46
56
  "scripts": {
47
- "build": "tsc -b tsconfig.build.json --force",
57
+ "build": "tsc -b tsconfig.build.json --force && vite build",
48
58
  "check": "tsc -b tsconfig.json && tsc -p test/tsconfig.json --noEmit",
49
59
  "lint": "eslint src test",
50
- "test": "vitest run"
60
+ "test": "vitest run",
61
+ "test:browser": "pnpm --filter '@knpkv/alchemy-console^...' build && pnpm build && playwright test"
51
62
  }
52
63
  }