devsess 0.0.0 → 0.2.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.
Files changed (42) hide show
  1. package/README.md +122 -0
  2. package/dist/async/define-cli.d.ts +22 -0
  3. package/dist/async/define-cli.d.ts.map +1 -0
  4. package/dist/async/dev-sessions.d.ts +21 -0
  5. package/dist/async/dev-sessions.d.ts.map +1 -0
  6. package/dist/async/index.d.ts +9 -0
  7. package/dist/async/index.d.ts.map +1 -0
  8. package/dist/async/index.js +141 -0
  9. package/dist/async/index.js.map +1 -0
  10. package/dist/async/platform.d.ts +41 -0
  11. package/dist/async/platform.d.ts.map +1 -0
  12. package/dist/async/run-dev-cli.d.ts +20 -0
  13. package/dist/async/run-dev-cli.d.ts.map +1 -0
  14. package/dist/async/run.d.ts +5 -0
  15. package/dist/async/run.d.ts.map +1 -0
  16. package/dist/async/session-state.d.ts +13 -0
  17. package/dist/async/session-state.d.ts.map +1 -0
  18. package/dist/async/session.d.ts +16 -0
  19. package/dist/async/session.d.ts.map +1 -0
  20. package/dist/chunk-AQGYVNYU.js +321 -0
  21. package/dist/chunk-AQGYVNYU.js.map +1 -0
  22. package/dist/current-session.d.ts +29 -0
  23. package/dist/current-session.d.ts.map +1 -0
  24. package/dist/dev/running-signal.d.ts +29 -0
  25. package/dist/dev/running-signal.d.ts.map +1 -0
  26. package/dist/dev/session-state.d.ts +23 -0
  27. package/dist/dev/session-state.d.ts.map +1 -0
  28. package/dist/dev/sticky-port.d.ts +4 -0
  29. package/dist/dev/sticky-port.d.ts.map +1 -0
  30. package/dist/dev/subprocess.d.ts +5 -0
  31. package/dist/dev/subprocess.d.ts.map +1 -0
  32. package/dist/dev-sessions.d.ts +60 -0
  33. package/dist/dev-sessions.d.ts.map +1 -0
  34. package/dist/index.d.ts +7 -0
  35. package/dist/index.d.ts.map +1 -0
  36. package/dist/index.js +43 -0
  37. package/dist/index.js.map +1 -0
  38. package/dist/pglite/index.d.ts +50 -0
  39. package/dist/pglite/index.d.ts.map +1 -0
  40. package/dist/pglite/index.js +218 -0
  41. package/dist/pglite/index.js.map +1 -0
  42. package/package.json +86 -1
package/README.md ADDED
@@ -0,0 +1,122 @@
1
+ # devsess
2
+
3
+ Scaffold dev scripts with reusable **dev sessions** and a per-session **PGlite + Drizzle** adapter — built on [Effect](https://effect.website).
4
+
5
+ `devsess` gives your local `scripts/dev.ts` two things:
6
+
7
+ - **Dev sessions** — each run reuses (or creates) a named, slug-based working directory under `.data/sessions`. Sticky dev-server ports, managed subprocesses that die with your script, and cross-service "running" signal files come built in.
8
+ - **A PGlite adapter** (`devsess/pglite`) — prepare a local Postgres-compatible database from your Drizzle migrations, scoped to the current dev session. Perfect for a `--lite` offline dev mode.
9
+
10
+ It runs on **Node or Bun** — devsess depends on neither directly, you supply whichever platform package's layer you use (`NodeServices.layer`/`BunServices.layer`, or the equivalent `platform` object for `devsess/async`). The examples below use the [Effect](https://effect.website) API (`devsess`); if you'd rather not deal with Effect directly, `devsess/async` exposes the same dev-session scaffolding as a plain, Promise-based API — see [API](#api).
11
+
12
+ ## Install
13
+
14
+ ```bash
15
+ bun add devsess effect @effect/platform-node
16
+ # or, on Bun: bun add devsess effect @effect/platform-bun
17
+ ```
18
+
19
+ For the PGlite adapter (`devsess/pglite`), also add:
20
+
21
+ ```bash
22
+ bun add @electric-sql/pglite drizzle-orm
23
+ ```
24
+
25
+ ## Quickstart
26
+
27
+ ```ts
28
+ // scripts/dev.ts
29
+ import { CurrentSession, DevSessions, getStickyPort, runManagedSubprocess } from 'devsess';
30
+ import { NodeRuntime, NodeServices } from '@effect/platform-node';
31
+ import { Effect } from 'effect';
32
+ import { Command, Flag } from 'effect/unstable/cli';
33
+
34
+ const web = Command.make(
35
+ 'web',
36
+ {
37
+ lite: Flag.boolean('lite').pipe(
38
+ Flag.withDescription('Use a per-session PGlite database'),
39
+ ),
40
+ },
41
+ (opts) =>
42
+ Effect.gen(function* () {
43
+ const session = yield* CurrentSession;
44
+ yield* Effect.logInfo(`[dev] session: ${session.name}`);
45
+
46
+ const port = yield* getStickyPort(session);
47
+ const env: Record<string, string> = { PORT: String(port) };
48
+
49
+ if (opts.lite) {
50
+ const litePath = yield* session.path('pglite');
51
+ env.DATABASE_LITE = 'true';
52
+ env.DATABASE_LITE_PATH = litePath;
53
+ }
54
+
55
+ yield* runManagedSubprocess('bunx', ['vite'], { env });
56
+ }).pipe(Effect.provide(CurrentSession.layer)),
57
+ );
58
+
59
+ Command.run(web, { version: '0.1.0' }).pipe(
60
+ Effect.provide(DevSessions.layer),
61
+ Effect.provide(NodeServices.layer),
62
+ Effect.scoped,
63
+ NodeRuntime.runMain,
64
+ );
65
+ ```
66
+
67
+ ```bash
68
+ bun scripts/dev.ts --lite
69
+ ```
70
+
71
+ `DevSessions.layer` auto-detects your project root (the nearest ancestor with a `package.json`) — no `dir` to pass. `CurrentSession.layer` sits on the handler, not around `Command.run`, so a `--help`/`--version` invocation never resolves (or creates) a session.
72
+
73
+ ## Per-session PGlite
74
+
75
+ `prepareSessionPglite` builds a migration dump (if missing), hydrates a PGlite
76
+ client, validates and applies migrations — all scoped to the active dev session:
77
+
78
+ ```ts
79
+ import { DevSessions, CurrentSession } from 'devsess';
80
+ import { prepareSessionPglite } from 'devsess/pglite';
81
+
82
+ // inside a handler, with CurrentSession.layer provided:
83
+ const sessions = yield* DevSessions;
84
+ const session = yield* CurrentSession;
85
+ const db = yield* prepareSessionPglite(session, {
86
+ migrationsFolder: sessions.path('drizzle'),
87
+ });
88
+ // db.client — a ready PGlite client
89
+ // db.dataDir — its on-disk path (e.g. pass as DATABASE_LITE_PATH)
90
+ ```
91
+
92
+ ## API
93
+
94
+ From `devsess` (the Effect API):
95
+
96
+ - `DevSessions` — session-store service. `.layer` auto-detects the project root; `.layerAt(rootDir)` overrides it
97
+ - `CurrentSession` — resolved-session service for the current run. `.layer` resolves via `DevSessions`; `.layerOf(session)` pins one
98
+ - `ProjectRootNotFoundError` — raised by `DevSessions.layer` when no ancestor of `process.cwd()` has a `package.json`
99
+ - `getStickyPort(session)`, `runManagedSubprocess(cmd, args, opts?)`, `publishRunning(data)`, `awaitRunning(pkg)` — free functions; only `getStickyPort` takes a `DevSession`
100
+ - `SessionState.slot(schema)` — typed per-session JSON state
101
+
102
+ Build your CLI with a stock `Command.make(...)` from `effect/unstable/cli` — devsess no longer wraps it.
103
+
104
+ From `devsess/pglite`:
105
+
106
+ - `prepareSessionPglite(session, { migrationsFolder })`
107
+ - `openLitePglite({ dataDir, dumpPath, migrationsFolder })`
108
+ - `buildPgliteDump`, `ensurePgliteDump`, `migratePglite`,
109
+ `dumpPgliteToFile`, `createPgliteFromDump`, `getDbMigrationCount`,
110
+ `getExpectedMigrationCount`, and the `PgliteError` tagged error.
111
+
112
+ From `devsess/async` (a plain, Promise-based mirror of the Effect API — no `effect` imports required):
113
+
114
+ - `defineDevCli({ name, dir, platform, options?, run })` — `run` is `async (ctx, opts) => void`, and `ctx`'s helpers return Promises instead of Effects
115
+ - `createDevSessions(rootDir, services)` — async session manager (`getSessions`, `createSession`, `getLatestOrCreate`)
116
+ - `SessionState`, `DevSession`, plus `cli` and `Schema` re-exports
117
+
118
+ See the [full documentation](https://github.com/fdarian/devsess/tree/main/apps/docs) for details.
119
+
120
+ ## License
121
+
122
+ Apache-2.0
@@ -0,0 +1,22 @@
1
+ import { Effect } from 'effect';
2
+ import { runManagedSubprocess } from '../dev/subprocess';
3
+ import type { DevPlatform } from './platform';
4
+ import { type CommandConfig } from './run-dev-cli';
5
+ import { type DevSession } from './session';
6
+ /** Promise-returning mirror of the Effect core's free functions (`getStickyPort`, `runManagedSubprocess`, `publishRunning`, `awaitRunning`), bundled into a single per-run context. */
7
+ type AsyncRunContext = {
8
+ session: () => Promise<DevSession>;
9
+ getStickyPort: () => Promise<number>;
10
+ runManagedSubprocess: (...args: Parameters<typeof runManagedSubprocess>) => Promise<Effect.Success<ReturnType<typeof runManagedSubprocess>>>;
11
+ publishRunning: (data: unknown) => Promise<void>;
12
+ awaitRunning: <T>(pkg: string) => Promise<T>;
13
+ };
14
+ export declare const defineDevCli: (config: {
15
+ name: string;
16
+ dir: string;
17
+ options?: CommandConfig;
18
+ platform: DevPlatform;
19
+ run: (ctx: AsyncRunContext, opts: Record<string, unknown>) => Promise<void> | void;
20
+ }) => ((argv: string[]) => void);
21
+ export {};
22
+ //# sourceMappingURL=define-cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"define-cli.d.ts","sourceRoot":"","sources":["../../src/async/define-cli.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAc,MAAM,QAAQ,CAAC;AAW5C,OAAO,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AAEzD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAC9C,OAAO,EAAE,KAAK,aAAa,EAAa,MAAM,eAAe,CAAC;AAC9D,OAAO,EAAE,KAAK,UAAU,EAAkB,MAAM,WAAW,CAAC;AAE5D,uLAAuL;AACvL,KAAK,eAAe,GAAG;IACtB,OAAO,EAAE,MAAM,OAAO,CAAC,UAAU,CAAC,CAAC;IACnC,aAAa,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;IACrC,oBAAoB,EAAE,CACrB,GAAG,IAAI,EAAE,UAAU,CAAC,OAAO,oBAAoB,CAAC,KAC5C,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,OAAO,oBAAoB,CAAC,CAAC,CAAC,CAAC;IACtE,cAAc,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACjD,YAAY,EAAE,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC;CAC7C,CAAC;AAEF,eAAO,MAAM,YAAY,WAAY;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,CAAC,EAAE,aAAa,CAAC;IACxB,QAAQ,EAAE,WAAW,CAAC;IACtB,GAAG,EAAE,CACJ,GAAG,EAAE,eAAe,EACpB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KACzB,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;CAC1B,KAAG,CAAC,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,IAAI,CA6C1B,CAAC"}
@@ -0,0 +1,21 @@
1
+ import { Layer } from 'effect';
2
+ import type { DevServices } from './platform';
3
+ import { type DevSession } from './session';
4
+ export type DevSessionManager = {
5
+ readonly dir: string;
6
+ getSessions(): Promise<Array<DevSession>>;
7
+ createSession(): Promise<DevSession>;
8
+ getLatestOrCreate(): Promise<DevSession>;
9
+ };
10
+ /**
11
+ * Creates a manager for slug-named dev session directories under
12
+ * `<rootDir>/.data/sessions`, using the caller-supplied platform `services` layer
13
+ * (e.g. `NodeServices.layer`). Same `rootDir` convention as `DevSessions.layerAt` — the
14
+ * CLI facade and this one agree on where sessions live given the same root.
15
+ *
16
+ * `DevSessions.layerAt` only wires lazy service methods when built (no directory IO
17
+ * happens at build time — see `src/dev-sessions.ts`), so it's cheap to re-provide the
18
+ * same `layer` value on every call rather than resolving a shared runtime once.
19
+ */
20
+ export declare const createDevSessions: (rootDir: string, services: Layer.Layer<DevServices>) => DevSessionManager;
21
+ //# sourceMappingURL=dev-sessions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dev-sessions.d.ts","sourceRoot":"","sources":["../../src/async/dev-sessions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAU,KAAK,EAAE,MAAM,QAAQ,CAAC;AAEvC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAC9C,OAAO,EAAE,KAAK,UAAU,EAAkB,MAAM,WAAW,CAAC;AAE5D,MAAM,MAAM,iBAAiB,GAAG;IAC/B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,WAAW,IAAI,OAAO,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC;IAC1C,aAAa,IAAI,OAAO,CAAC,UAAU,CAAC,CAAC;IACrC,iBAAiB,IAAI,OAAO,CAAC,UAAU,CAAC,CAAC;CACzC,CAAC;AAEF;;;;;;;;;GASG;AACH,eAAO,MAAM,iBAAiB,YACpB,MAAM,YACL,KAAK,CAAC,KAAK,CAAC,WAAW,CAAC,KAChC,iBAsBF,CAAC"}
@@ -0,0 +1,9 @@
1
+ export { Schema } from 'effect';
2
+ export * as cli from 'effect/unstable/cli';
3
+ export { SessionStateError } from '../dev/session-state';
4
+ export { defineDevCli } from './define-cli';
5
+ export { createDevSessions, type DevSessionManager } from './dev-sessions';
6
+ export type { DevPlatform, DevServices } from './platform';
7
+ export type { DevSession } from './session';
8
+ export { SessionState } from './session-state';
9
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/async/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChC,OAAO,KAAK,GAAG,MAAM,qBAAqB,CAAC;AAE3C,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AACzD,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,iBAAiB,EAAE,KAAK,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAC3E,YAAY,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAC3D,YAAY,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAC5C,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC"}
@@ -0,0 +1,141 @@
1
+ import {
2
+ DevSessions,
3
+ SessionState,
4
+ SessionStateError,
5
+ awaitRunningSignal,
6
+ getStickyPort,
7
+ publishRunningSignal,
8
+ resolveSiblingDir,
9
+ runManagedSubprocess,
10
+ runningSignalPath
11
+ } from "../chunk-AQGYVNYU.js";
12
+
13
+ // src/async/index.ts
14
+ import { Schema } from "effect";
15
+ import * as cli2 from "effect/unstable/cli";
16
+
17
+ // src/async/define-cli.ts
18
+ import { Effect as Effect3 } from "effect";
19
+
20
+ // src/async/run-dev-cli.ts
21
+ import { Effect, Layer } from "effect";
22
+ import * as cli from "effect/unstable/cli";
23
+ var runDevCli = (config) => {
24
+ const command = cli.Command.make(
25
+ config.name,
26
+ config.options ?? {},
27
+ (opts) => config.makeHandler(opts)
28
+ );
29
+ return (argv) => {
30
+ const layer = DevSessions.layerAt(config.dir).pipe(
31
+ Layer.provideMerge(config.platform.services)
32
+ );
33
+ const program = cli.Command.runWith(command, {
34
+ version: "0.0.0"
35
+ })(argv);
36
+ config.platform.runMain(Effect.scoped(Effect.provide(program, layer)));
37
+ };
38
+ };
39
+
40
+ // src/async/session.ts
41
+ import { Effect as Effect2 } from "effect";
42
+ var EFFECT_SESSION = /* @__PURE__ */ Symbol("devsess/async/DevSession#effectSession");
43
+ var toAsyncSession = (session) => {
44
+ const asyncSession = {
45
+ name: session.name,
46
+ lastModifiedAt: session.lastModifiedAt,
47
+ // Safe to run synchronously: the core `path` is `Effect.succeed(pathJoin(rel))`, pure.
48
+ path: (relativePath) => Effect2.runSync(session.path(relativePath)),
49
+ toString: () => session.toString()
50
+ };
51
+ Object.defineProperty(asyncSession, EFFECT_SESSION, {
52
+ value: session,
53
+ enumerable: false
54
+ });
55
+ return asyncSession;
56
+ };
57
+ var unwrapSession = (session) => {
58
+ const effectSession = session[EFFECT_SESSION];
59
+ if (effectSession === void 0) {
60
+ throw new Error(
61
+ "devsess/async: expected a DevSession created by this package, but the internal Effect session handle is missing"
62
+ );
63
+ }
64
+ return effectSession;
65
+ };
66
+
67
+ // src/async/define-cli.ts
68
+ var defineDevCli = (config) => runDevCli({
69
+ name: config.name,
70
+ dir: config.dir,
71
+ options: config.options,
72
+ platform: config.platform,
73
+ makeHandler: (opts) => Effect3.gen(function* () {
74
+ const sessions = yield* DevSessions;
75
+ const session = yield* Effect3.cached(sessions.getLatestOrCreate);
76
+ const services = yield* Effect3.context();
77
+ const ctx = {
78
+ session: () => Effect3.runPromiseWith(services)(session).then(toAsyncSession),
79
+ getStickyPort: () => Effect3.runPromiseWith(services)(
80
+ Effect3.flatMap(session, getStickyPort)
81
+ ),
82
+ runManagedSubprocess: (...args) => Effect3.runPromiseWith(services)(runManagedSubprocess(...args)),
83
+ publishRunning: (data) => Effect3.runPromiseWith(services)(
84
+ publishRunningSignal(runningSignalPath(config.dir), data)
85
+ ),
86
+ awaitRunning: (pkg) => Effect3.runPromiseWith(services)(
87
+ awaitRunningSignal(
88
+ runningSignalPath(resolveSiblingDir(pkg, config.dir)),
89
+ { parse: (raw) => JSON.parse(raw) }
90
+ )
91
+ )
92
+ };
93
+ yield* Effect3.promise(() => Promise.resolve(config.run(ctx, opts)));
94
+ })
95
+ });
96
+
97
+ // src/async/dev-sessions.ts
98
+ import { Effect as Effect4, Layer as Layer2 } from "effect";
99
+ var createDevSessions = (rootDir, services) => {
100
+ const layer = DevSessions.layerAt(rootDir).pipe(Layer2.provide(services));
101
+ const run2 = (effect) => Effect4.runPromise(Effect4.provide(effect, layer));
102
+ return {
103
+ // Matches the service's own `dir`, which is `rootDir` verbatim — safe to read synchronously.
104
+ dir: rootDir,
105
+ getSessions: () => run2(DevSessions.use((sessions) => sessions.getSessions)).then(
106
+ (sessions) => sessions.map(toAsyncSession)
107
+ ),
108
+ createSession: () => run2(DevSessions.use((sessions) => sessions.createSession)).then(
109
+ toAsyncSession
110
+ ),
111
+ getLatestOrCreate: () => run2(DevSessions.use((sessions) => sessions.getLatestOrCreate)).then(
112
+ toAsyncSession
113
+ )
114
+ };
115
+ };
116
+
117
+ // src/async/run.ts
118
+ import { Effect as Effect5 } from "effect";
119
+ var run = (effect, services) => Effect5.runPromise(Effect5.scoped(Effect5.provide(effect, services)));
120
+
121
+ // src/async/session-state.ts
122
+ var SessionState2;
123
+ ((SessionState3) => {
124
+ function slot(schema, services) {
125
+ const effectSlot = SessionState.slot(schema);
126
+ return {
127
+ read: (session) => run(effectSlot.read(unwrapSession(session)), services),
128
+ write: (session, data) => run(effectSlot.write(unwrapSession(session), data), services)
129
+ };
130
+ }
131
+ SessionState3.slot = slot;
132
+ })(SessionState2 || (SessionState2 = {}));
133
+ export {
134
+ Schema,
135
+ SessionState2 as SessionState,
136
+ SessionStateError,
137
+ cli2 as cli,
138
+ createDevSessions,
139
+ defineDevCli
140
+ };
141
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/async/index.ts","../../src/async/define-cli.ts","../../src/async/run-dev-cli.ts","../../src/async/session.ts","../../src/async/dev-sessions.ts","../../src/async/run.ts","../../src/async/session-state.ts"],"sourcesContent":["// Re-exported so consumers can declare CLI flags/arguments without a direct\n// dependency on `effect/unstable/cli`, e.g. `cli.Flag.string('local')`.\n\nexport { Schema } from 'effect';\nexport * as cli from 'effect/unstable/cli';\n\nexport { SessionStateError } from '../dev/session-state';\nexport { defineDevCli } from './define-cli';\nexport { createDevSessions, type DevSessionManager } from './dev-sessions';\nexport type { DevPlatform, DevServices } from './platform';\nexport type { DevSession } from './session';\nexport { SessionState } from './session-state';\n","import { Effect, type Scope } from 'effect';\nimport type { FileSystem } from 'effect/FileSystem';\nimport type { Path } from 'effect/Path';\nimport type { ChildProcessSpawner } from 'effect/unstable/process/ChildProcessSpawner';\nimport {\n\tawaitRunningSignal,\n\tpublishRunningSignal,\n\tresolveSiblingDir,\n\trunningSignalPath,\n} from '../dev/running-signal';\nimport { getStickyPort } from '../dev/sticky-port';\nimport { runManagedSubprocess } from '../dev/subprocess';\nimport { DevSessions } from '../dev-sessions';\nimport type { DevPlatform } from './platform';\nimport { type CommandConfig, runDevCli } from './run-dev-cli';\nimport { type DevSession, toAsyncSession } from './session';\n\n/** Promise-returning mirror of the Effect core's free functions (`getStickyPort`, `runManagedSubprocess`, `publishRunning`, `awaitRunning`), bundled into a single per-run context. */\ntype AsyncRunContext = {\n\tsession: () => Promise<DevSession>;\n\tgetStickyPort: () => Promise<number>;\n\trunManagedSubprocess: (\n\t\t...args: Parameters<typeof runManagedSubprocess>\n\t) => Promise<Effect.Success<ReturnType<typeof runManagedSubprocess>>>;\n\tpublishRunning: (data: unknown) => Promise<void>;\n\tawaitRunning: <T>(pkg: string) => Promise<T>;\n};\n\nexport const defineDevCli = (config: {\n\tname: string;\n\tdir: string;\n\toptions?: CommandConfig;\n\tplatform: DevPlatform;\n\trun: (\n\t\tctx: AsyncRunContext,\n\t\topts: Record<string, unknown>,\n\t) => Promise<void> | void;\n}): ((argv: string[]) => void) =>\n\trunDevCli({\n\t\tname: config.name,\n\t\tdir: config.dir,\n\t\toptions: config.options,\n\t\tplatform: config.platform,\n\t\tmakeHandler: (opts) =>\n\t\t\tEffect.gen(function* () {\n\t\t\t\tconst sessions = yield* DevSessions;\n\t\t\t\tconst session = yield* Effect.cached(sessions.getLatestOrCreate);\n\n\t\t\t\t// Capture the process-lifetime context (the Scope opened by `runDevCli`'s\n\t\t\t\t// outer `Effect.scoped`, plus DevSessions | FileSystem | Path | ChildProcessSpawner).\n\t\t\t\t// Running each ctx helper with this captured context — instead of a fresh,\n\t\t\t\t// transient `Effect.runPromise` per call — attaches its scoped resources\n\t\t\t\t// (managed subprocess, running-signal file) to that process-lifetime scope,\n\t\t\t\t// so they only release when the CLI process ends, not right after the Promise settles.\n\t\t\t\tconst services = yield* Effect.context<\n\t\t\t\t\tDevSessions | FileSystem | Path | ChildProcessSpawner | Scope.Scope\n\t\t\t\t>();\n\n\t\t\t\tconst ctx: AsyncRunContext = {\n\t\t\t\t\tsession: () =>\n\t\t\t\t\t\tEffect.runPromiseWith(services)(session).then(toAsyncSession),\n\t\t\t\t\tgetStickyPort: () =>\n\t\t\t\t\t\tEffect.runPromiseWith(services)(\n\t\t\t\t\t\t\tEffect.flatMap(session, getStickyPort),\n\t\t\t\t\t\t),\n\t\t\t\t\trunManagedSubprocess: (...args) =>\n\t\t\t\t\t\tEffect.runPromiseWith(services)(runManagedSubprocess(...args)),\n\t\t\t\t\tpublishRunning: (data) =>\n\t\t\t\t\t\tEffect.runPromiseWith(services)(\n\t\t\t\t\t\t\tpublishRunningSignal(runningSignalPath(config.dir), data),\n\t\t\t\t\t\t),\n\t\t\t\t\tawaitRunning: <T>(pkg: string) =>\n\t\t\t\t\t\tEffect.runPromiseWith(services)(\n\t\t\t\t\t\t\tawaitRunningSignal<T>(\n\t\t\t\t\t\t\t\trunningSignalPath(resolveSiblingDir(pkg, config.dir)),\n\t\t\t\t\t\t\t\t{ parse: (raw) => JSON.parse(raw) as T },\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t),\n\t\t\t\t};\n\n\t\t\t\tyield* Effect.promise(() => Promise.resolve(config.run(ctx, opts)));\n\t\t\t}),\n\t});\n","import { Effect, Layer, type Scope } from 'effect';\nimport * as cli from 'effect/unstable/cli';\nimport { DevSessions } from '../dev-sessions';\nimport type { DevPlatform, DevServices } from './platform';\n\nexport type CommandConfig = typeof cli.Command.make extends (\n\tname: string,\n\tconfig: infer C,\n\t...rest: Array<unknown>\n) => unknown\n\t? C\n\t: never;\n\ntype CliHandler = (\n\topts: Record<string, unknown>,\n) => Effect.Effect<void, unknown, DevSessions | DevServices | Scope.Scope>;\n\n/**\n * CLI scaffolding for the plain-async `defineDevCli` facade (`src/async/define-cli.ts`):\n * builds the `cli.Command`, wires the per-project `DevSessions` + caller-supplied\n * platform services layer, and runs it via `platform.runMain`.\n */\nexport const runDevCli = (config: {\n\tname: string;\n\tdir: string;\n\toptions?: CommandConfig;\n\tplatform: DevPlatform;\n\tmakeHandler: CliHandler;\n}): ((argv: string[]) => void) => {\n\tconst command = cli.Command.make(config.name, config.options ?? {}, (opts) =>\n\t\tconfig.makeHandler(opts as Record<string, unknown>),\n\t);\n\n\treturn (argv) => {\n\t\tconst layer = DevSessions.layerAt(config.dir).pipe(\n\t\t\tLayer.provideMerge(config.platform.services),\n\t\t);\n\n\t\tconst program = cli.Command.runWith(command, {\n\t\t\tversion: '0.0.0',\n\t\t})(argv);\n\n\t\tconfig.platform.runMain(Effect.scoped(Effect.provide(program, layer)));\n\t};\n};\n","import { Effect } from 'effect';\nimport type { DevSession as EffectDevSession } from '../dev-sessions';\n\n/** Non-enumerable, module-private key used to stash the Effect session on its async wrapper. */\nconst EFFECT_SESSION = Symbol('devsess/async/DevSession#effectSession');\n\nexport type DevSession = {\n\treadonly name: string;\n\treadonly lastModifiedAt: Date | null;\n\tpath(relativePath: string): string;\n\ttoString(): string;\n};\n\n/**\n * Wraps an Effect `DevSession` as a plain-async `DevSession`. The original Effect\n * session is stashed under `EFFECT_SESSION` so other async ops (e.g. `SessionState`)\n * can reuse the Effect core via `unwrapSession`.\n */\nexport const toAsyncSession = (session: EffectDevSession): DevSession => {\n\tconst asyncSession: DevSession = {\n\t\tname: session.name,\n\t\tlastModifiedAt: session.lastModifiedAt,\n\t\t// Safe to run synchronously: the core `path` is `Effect.succeed(pathJoin(rel))`, pure.\n\t\tpath: (relativePath) => Effect.runSync(session.path(relativePath)),\n\t\ttoString: () => session.toString(),\n\t};\n\n\tObject.defineProperty(asyncSession, EFFECT_SESSION, {\n\t\tvalue: session,\n\t\tenumerable: false,\n\t});\n\n\treturn asyncSession;\n};\n\n/** Recovers the Effect `DevSession` stashed by `toAsyncSession`. */\nexport const unwrapSession = (session: DevSession): EffectDevSession => {\n\tconst effectSession = (\n\t\tsession as unknown as Record<\n\t\t\ttypeof EFFECT_SESSION,\n\t\t\tEffectDevSession | undefined\n\t\t>\n\t)[EFFECT_SESSION];\n\tif (effectSession === undefined) {\n\t\tthrow new Error(\n\t\t\t'devsess/async: expected a DevSession created by this package, but the internal Effect session handle is missing',\n\t\t);\n\t}\n\treturn effectSession;\n};\n","import { Effect, Layer } from 'effect';\nimport { DevSessions } from '../dev-sessions';\nimport type { DevServices } from './platform';\nimport { type DevSession, toAsyncSession } from './session';\n\nexport type DevSessionManager = {\n\treadonly dir: string;\n\tgetSessions(): Promise<Array<DevSession>>;\n\tcreateSession(): Promise<DevSession>;\n\tgetLatestOrCreate(): Promise<DevSession>;\n};\n\n/**\n * Creates a manager for slug-named dev session directories under\n * `<rootDir>/.data/sessions`, using the caller-supplied platform `services` layer\n * (e.g. `NodeServices.layer`). Same `rootDir` convention as `DevSessions.layerAt` — the\n * CLI facade and this one agree on where sessions live given the same root.\n *\n * `DevSessions.layerAt` only wires lazy service methods when built (no directory IO\n * happens at build time — see `src/dev-sessions.ts`), so it's cheap to re-provide the\n * same `layer` value on every call rather than resolving a shared runtime once.\n */\nexport const createDevSessions = (\n\trootDir: string,\n\tservices: Layer.Layer<DevServices>,\n): DevSessionManager => {\n\tconst layer = DevSessions.layerAt(rootDir).pipe(Layer.provide(services));\n\n\tconst run = <A, E>(effect: Effect.Effect<A, E, DevSessions>): Promise<A> =>\n\t\tEffect.runPromise(Effect.provide(effect, layer));\n\n\treturn {\n\t\t// Matches the service's own `dir`, which is `rootDir` verbatim — safe to read synchronously.\n\t\tdir: rootDir,\n\t\tgetSessions: () =>\n\t\t\trun(DevSessions.use((sessions) => sessions.getSessions)).then(\n\t\t\t\t(sessions) => sessions.map(toAsyncSession),\n\t\t\t),\n\t\tcreateSession: () =>\n\t\t\trun(DevSessions.use((sessions) => sessions.createSession)).then(\n\t\t\t\ttoAsyncSession,\n\t\t\t),\n\t\tgetLatestOrCreate: () =>\n\t\t\trun(DevSessions.use((sessions) => sessions.getLatestOrCreate)).then(\n\t\t\t\ttoAsyncSession,\n\t\t\t),\n\t};\n};\n","import { Effect, type Layer, type Scope } from 'effect';\nimport type { DevServices } from './platform';\n\n/** Runs an Effect that only needs the caller-supplied platform services (+ an optional Scope) as a Promise. */\nexport const run = <A, E>(\n\teffect: Effect.Effect<A, E, DevServices | Scope.Scope>,\n\tservices: Layer.Layer<DevServices>,\n): Promise<A> =>\n\tEffect.runPromise(Effect.scoped(Effect.provide(effect, services)));\n","import type { Layer } from 'effect';\nimport type * as S from 'effect/Schema';\nimport { SessionState as EffectSessionState } from '../dev/session-state';\nimport type { DevServices } from './platform';\nimport { run } from './run';\nimport { type DevSession, unwrapSession } from './session';\n\nexport namespace SessionState {\n\t// Decoding must run without extra context beyond the platform `services` layer, so\n\t// schemas cannot require decoding services of their own.\n\ttype Shape = S.Codec<unknown, unknown, never>;\n\n\texport function slot<T extends Shape>(\n\t\tschema: T,\n\t\tservices: Layer.Layer<DevServices>,\n\t) {\n\t\tconst effectSlot = EffectSessionState.slot(schema);\n\t\treturn {\n\t\t\tread: (session: DevSession): Promise<T['Type'] | null> =>\n\t\t\t\trun(effectSlot.read(unwrapSession(session)), services),\n\t\t\twrite: (session: DevSession, data: T['Type']): Promise<void> =>\n\t\t\t\trun(effectSlot.write(unwrapSession(session), data), services),\n\t\t};\n\t}\n}\n"],"mappings":"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","names":["cli","Effect","Effect","Effect","Effect","Layer","Layer","run","Effect","Effect","SessionState"]}
@@ -0,0 +1,41 @@
1
+ import type { Effect, Layer } from 'effect';
2
+ import type { FileSystem } from 'effect/FileSystem';
3
+ import type { Path } from 'effect/Path';
4
+ import type * as Runtime from 'effect/Runtime';
5
+ import type { Stdio } from 'effect/Stdio';
6
+ import type { Terminal } from 'effect/Terminal';
7
+ import type { ChildProcessSpawner } from 'effect/unstable/process/ChildProcessSpawner';
8
+ /**
9
+ * Core `effect` service tags this library needs from the caller-supplied platform.
10
+ * `effect/unstable/cli`'s `Command.runWith` pulls in `Stdio`/`Terminal` alongside the
11
+ * `FileSystem`/`Path`/`ChildProcessSpawner` the library's own code depends on directly.
12
+ *
13
+ * Both `@effect/platform-node`'s `NodeServices.NodeServices` and `@effect/platform-bun`'s
14
+ * `BunServices.BunServices` are unions of these same core tags (plus `Crypto`, which this
15
+ * library never uses), so either `.layer` satisfies this narrower requirement.
16
+ */
17
+ export type DevServices = ChildProcessSpawner | FileSystem | Path | Stdio | Terminal;
18
+ type RunMainOptions = {
19
+ readonly disableErrorReporting?: boolean | undefined;
20
+ readonly teardown?: Runtime.Teardown | undefined;
21
+ };
22
+ /**
23
+ * Structurally identical to `NodeRuntime.runMain` and `BunRuntime.runMain` (both are the
24
+ * same function, re-exported from `@effect/platform-node-shared`), so either is assignable
25
+ * here with no cast.
26
+ */
27
+ type RunMain = {
28
+ (options?: RunMainOptions): <E, A>(effect: Effect.Effect<A, E>) => void;
29
+ <E, A>(effect: Effect.Effect<A, E>, options?: RunMainOptions): void;
30
+ };
31
+ /**
32
+ * The platform a caller supplies so the library depends on no platform package of its
33
+ * own, e.g. `{ services: NodeServices.layer, runMain: NodeRuntime.runMain }` or the `Bun`
34
+ * equivalents.
35
+ */
36
+ export type DevPlatform = {
37
+ readonly services: Layer.Layer<DevServices>;
38
+ readonly runMain: RunMain;
39
+ };
40
+ export {};
41
+ //# sourceMappingURL=platform.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"platform.d.ts","sourceRoot":"","sources":["../../src/async/platform.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAC;AAC5C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,KAAK,KAAK,OAAO,MAAM,gBAAgB,CAAC;AAC/C,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,6CAA6C,CAAC;AAEvF;;;;;;;;GAQG;AACH,MAAM,MAAM,WAAW,GACpB,mBAAmB,GACnB,UAAU,GACV,IAAI,GACJ,KAAK,GACL,QAAQ,CAAC;AAEZ,KAAK,cAAc,GAAG;IACrB,QAAQ,CAAC,qBAAqB,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IACrD,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC,QAAQ,GAAG,SAAS,CAAC;CACjD,CAAC;AAEF;;;;GAIG;AACH,KAAK,OAAO,GAAG;IACd,CAAC,OAAO,CAAC,EAAE,cAAc,GAAG,CAAC,CAAC,EAAE,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,IAAI,CAAC;IACxE,CAAC,CAAC,EAAE,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,OAAO,CAAC,EAAE,cAAc,GAAG,IAAI,CAAC;CACpE,CAAC;AAEF;;;;GAIG;AACH,MAAM,MAAM,WAAW,GAAG;IACzB,QAAQ,CAAC,QAAQ,EAAE,KAAK,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;IAC5C,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;CAC1B,CAAC"}
@@ -0,0 +1,20 @@
1
+ import { Effect, type Scope } from 'effect';
2
+ import * as cli from 'effect/unstable/cli';
3
+ import { DevSessions } from '../dev-sessions';
4
+ import type { DevPlatform, DevServices } from './platform';
5
+ export type CommandConfig = typeof cli.Command.make extends (name: string, config: infer C, ...rest: Array<unknown>) => unknown ? C : never;
6
+ type CliHandler = (opts: Record<string, unknown>) => Effect.Effect<void, unknown, DevSessions | DevServices | Scope.Scope>;
7
+ /**
8
+ * CLI scaffolding for the plain-async `defineDevCli` facade (`src/async/define-cli.ts`):
9
+ * builds the `cli.Command`, wires the per-project `DevSessions` + caller-supplied
10
+ * platform services layer, and runs it via `platform.runMain`.
11
+ */
12
+ export declare const runDevCli: (config: {
13
+ name: string;
14
+ dir: string;
15
+ options?: CommandConfig;
16
+ platform: DevPlatform;
17
+ makeHandler: CliHandler;
18
+ }) => ((argv: string[]) => void);
19
+ export {};
20
+ //# sourceMappingURL=run-dev-cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run-dev-cli.d.ts","sourceRoot":"","sources":["../../src/async/run-dev-cli.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAS,KAAK,KAAK,EAAE,MAAM,QAAQ,CAAC;AACnD,OAAO,KAAK,GAAG,MAAM,qBAAqB,CAAC;AAC3C,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAC9C,OAAO,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE3D,MAAM,MAAM,aAAa,GAAG,OAAO,GAAG,CAAC,OAAO,CAAC,IAAI,SAAS,CAC3D,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,CAAC,EACf,GAAG,IAAI,EAAE,KAAK,CAAC,OAAO,CAAC,KACnB,OAAO,GACT,CAAC,GACD,KAAK,CAAC;AAET,KAAK,UAAU,GAAG,CACjB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KACzB,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,OAAO,EAAE,WAAW,GAAG,WAAW,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC;AAE3E;;;;GAIG;AACH,eAAO,MAAM,SAAS,WAAY;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,CAAC,EAAE,aAAa,CAAC;IACxB,QAAQ,EAAE,WAAW,CAAC;IACtB,WAAW,EAAE,UAAU,CAAC;CACxB,KAAG,CAAC,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,IAAI,CAgB5B,CAAC"}
@@ -0,0 +1,5 @@
1
+ import { Effect, type Layer, type Scope } from 'effect';
2
+ import type { DevServices } from './platform';
3
+ /** Runs an Effect that only needs the caller-supplied platform services (+ an optional Scope) as a Promise. */
4
+ export declare const run: <A, E>(effect: Effect.Effect<A, E, DevServices | Scope.Scope>, services: Layer.Layer<DevServices>) => Promise<A>;
5
+ //# sourceMappingURL=run.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../src/async/run.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,KAAK,KAAK,EAAE,KAAK,KAAK,EAAE,MAAM,QAAQ,CAAC;AACxD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C,+GAA+G;AAC/G,eAAO,MAAM,GAAG,GAAI,CAAC,EAAE,CAAC,UACf,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,WAAW,GAAG,KAAK,CAAC,KAAK,CAAC,YAC5C,KAAK,CAAC,KAAK,CAAC,WAAW,CAAC,KAChC,OAAO,CAAC,CAAC,CACuD,CAAC"}
@@ -0,0 +1,13 @@
1
+ import type { Layer } from 'effect';
2
+ import type * as S from 'effect/Schema';
3
+ import type { DevServices } from './platform';
4
+ import { type DevSession } from './session';
5
+ export declare namespace SessionState {
6
+ type Shape = S.Codec<unknown, unknown, never>;
7
+ export function slot<T extends Shape>(schema: T, services: Layer.Layer<DevServices>): {
8
+ read: (session: DevSession) => Promise<T['Type'] | null>;
9
+ write: (session: DevSession, data: T['Type']) => Promise<void>;
10
+ };
11
+ export {};
12
+ }
13
+ //# sourceMappingURL=session-state.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-state.d.ts","sourceRoot":"","sources":["../../src/async/session-state.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAC;AACpC,OAAO,KAAK,KAAK,CAAC,MAAM,eAAe,CAAC;AAExC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C,OAAO,EAAE,KAAK,UAAU,EAAiB,MAAM,WAAW,CAAC;AAE3D,yBAAiB,YAAY,CAAC;IAG7B,KAAK,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC;IAE9C,MAAM,UAAU,IAAI,CAAC,CAAC,SAAS,KAAK,EACnC,MAAM,EAAE,CAAC,EACT,QAAQ,EAAE,KAAK,CAAC,KAAK,CAAC,WAAW,CAAC;QAIjC,IAAI,YAAY,UAAU,KAAG,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC;QAEtD,KAAK,YAAY,UAAU,QAAQ,CAAC,CAAC,MAAM,CAAC,KAAG,OAAO,CAAC,IAAI,CAAC;MAG7D;;CACD"}
@@ -0,0 +1,16 @@
1
+ import type { DevSession as EffectDevSession } from '../dev-sessions';
2
+ export type DevSession = {
3
+ readonly name: string;
4
+ readonly lastModifiedAt: Date | null;
5
+ path(relativePath: string): string;
6
+ toString(): string;
7
+ };
8
+ /**
9
+ * Wraps an Effect `DevSession` as a plain-async `DevSession`. The original Effect
10
+ * session is stashed under `EFFECT_SESSION` so other async ops (e.g. `SessionState`)
11
+ * can reuse the Effect core via `unwrapSession`.
12
+ */
13
+ export declare const toAsyncSession: (session: EffectDevSession) => DevSession;
14
+ /** Recovers the Effect `DevSession` stashed by `toAsyncSession`. */
15
+ export declare const unwrapSession: (session: DevSession) => EffectDevSession;
16
+ //# sourceMappingURL=session.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../src/async/session.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,IAAI,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAKtE,MAAM,MAAM,UAAU,GAAG;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,cAAc,EAAE,IAAI,GAAG,IAAI,CAAC;IACrC,IAAI,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAAC;IACnC,QAAQ,IAAI,MAAM,CAAC;CACnB,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,cAAc,YAAa,gBAAgB,KAAG,UAe1D,CAAC;AAEF,oEAAoE;AACpE,eAAO,MAAM,aAAa,YAAa,UAAU,KAAG,gBAanD,CAAC"}