@opencraw/studio 0.0.2

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 (32) hide show
  1. package/README.md +11 -0
  2. package/bin/opencraw-studio.mjs +4 -0
  3. package/dist/index.d.ts +1 -0
  4. package/dist/index.esm.js +886 -0
  5. package/dist/src/index.d.ts +10 -0
  6. package/dist/src/main.d.ts +11 -0
  7. package/dist/src/page-snapshot/fetch-start-page.use-case.d.ts +20 -0
  8. package/dist/src/page-snapshot/index.d.ts +2 -0
  9. package/dist/src/recipe-workspace/index.d.ts +3 -0
  10. package/dist/src/recipe-workspace/open-workspace.use-case.d.ts +15 -0
  11. package/dist/src/recipe-workspace/save-recipe.use-case.d.ts +16 -0
  12. package/dist/src/sample-run/index.d.ts +6 -0
  13. package/dist/src/sample-run/load-recipe-pair.use-case.d.ts +18 -0
  14. package/dist/src/sample-run/run-sample.use-case.d.ts +30 -0
  15. package/dist/src/sample-run/sample-run-record.contract.d.ts +19 -0
  16. package/dist/src/studio-api/command.contract.d.ts +67 -0
  17. package/dist/src/studio-api/event.contract.d.ts +70 -0
  18. package/dist/src/studio-api/index.d.ts +11 -0
  19. package/dist/src/studio-api/sample-run-view.contract.d.ts +28 -0
  20. package/dist/src/studio-api/start-page-view.contract.d.ts +7 -0
  21. package/dist/src/studio-api/workspace-view.contract.d.ts +55 -0
  22. package/dist/src/studio-server/access-token.policy.d.ts +26 -0
  23. package/dist/src/studio-server/fetch-start-page.handler.d.ts +14 -0
  24. package/dist/src/studio-server/index.d.ts +3 -0
  25. package/dist/src/studio-server/open-workspace.handler.d.ts +13 -0
  26. package/dist/src/studio-server/run-sample.handler.d.ts +28 -0
  27. package/dist/src/studio-server/save-recipe.handler.d.ts +15 -0
  28. package/dist/src/studio-server/static-file.handler.d.ts +14 -0
  29. package/dist/src/studio-server/studio-http.use-case.d.ts +32 -0
  30. package/dist/src/studio-server/websocket.client.d.ts +22 -0
  31. package/dist/src/studio-server/workspace.store.d.ts +21 -0
  32. package/package.json +80 -0
@@ -0,0 +1,10 @@
1
+ export { main } from './main.js';
2
+ export { startStudioServer } from './studio-server/index.js';
3
+ export type { StudioServer, StudioServerOptions } from './studio-server/index.js';
4
+ export { openWorkspace, saveRecipe } from './recipe-workspace/index.js';
5
+ export { runSample, loadRecipePair } from './sample-run/index.js';
6
+ export type { RecipePair, SampleRunCallbacks, SampleRunHandle, SampleRunRecord, SampleRunResult, SampleStoppedBy } from './sample-run/index.js';
7
+ export { fetchStartPage } from './page-snapshot/index.js';
8
+ export { sampleBudgetSchema, openWorkspaceCommandSchema, runSampleCommandSchema, stopRunCommandSchema, saveRecipeCommandSchema, fetchStartPageCommandSchema, studioCommandSchema, stoppedBySchema, traceLineEventSchema, recordEventSchema, runFinishedEventSchema, workspaceChangedEventSchema, studioEventSchema, recipeIssueSchema, recipeListingSchema, workspaceViewSchema, sampleRunRecordSchema, sampleRunViewSchema, startPageViewSchema, } from './studio-api/index.js';
9
+ export type { SampleBudget, OpenWorkspaceCommand, RunSampleCommand, StopRunCommand, SaveRecipeCommand, FetchStartPageCommand, StudioCommand, StoppedBy, TraceLineEvent, RecordEvent, RunFinishedEvent, WorkspaceChangedEvent, StudioEvent, RecipeIssue, RecipeListing, WorkspaceView, SampleRunView, StartPageView, } from './studio-api/index.js';
10
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Runs `opencraw-studio`: starts the server, opens `folder` (if given) as
3
+ * the workspace right away, and prints the URL with the token, the way
4
+ * `docs/research/recipe-studio.md` §2 shows it: `http://127.0.0.1:<port>/?token=…`.
5
+ * The process stays up (the server keeps the event loop alive) until it is
6
+ * interrupted (Ctrl+C).
7
+ *
8
+ * @param argv - Arguments after the program name: an optional recipe folder, `.` when omitted.
9
+ */
10
+ export declare function main(argv: readonly string[]): Promise<void>;
11
+ //# sourceMappingURL=main.d.ts.map
@@ -0,0 +1,20 @@
1
+ import type { BrowserSessionConfig, InputRecipe } from '@opencraw/core';
2
+ /**
3
+ * Fetches an input recipe's start page as the engine would see it: a fresh
4
+ * Playwright page in web mode, core's HTTP client in api mode. Returns plain
5
+ * text: the page's HTML, or a readable rendering of a non-HTML api response.
6
+ * No rewriting, no script stripping, no node ids yet: that is phase 2's
7
+ * `page-snapshot` proper (studio plan, #91). Provisional for phase 0 in two
8
+ * ways, both worth knowing before relying on it: it opens a bare page
9
+ * without running the recipe's own steps first, so a start point behind a
10
+ * `session.bootstrap` login shows what an anonymous visitor sees; and it
11
+ * launches a fresh browser or HTTP context per call, one page then closes
12
+ * it, rather than reusing whatever the last sample run had open.
13
+ *
14
+ * @param input - The input recipe; only `mode` and the first `start` point are used.
15
+ * @param browser - Browser launch settings for web mode (an executable path override, headless…).
16
+ * @returns The page's content as plain text.
17
+ * @throws Error when the recipe has no start point.
18
+ */
19
+ export declare function fetchStartPage(input: InputRecipe, browser?: BrowserSessionConfig): Promise<string>;
20
+ //# sourceMappingURL=fetch-start-page.use-case.d.ts.map
@@ -0,0 +1,2 @@
1
+ export { fetchStartPage } from './fetch-start-page.use-case.js';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,3 @@
1
+ export { openWorkspace } from './open-workspace.use-case.js';
2
+ export { saveRecipe } from './save-recipe.use-case.js';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,15 @@
1
+ import type { WorkspaceView } from '../studio-api/index.js';
2
+ /**
3
+ * Opens a recipe folder: every `.json`/`.jsonl` recipe directly inside it
4
+ * (core's own directory reading, so relative `file:` URLs resolve the same
5
+ * way a real run would), parsed and, for each output recipe found, bound to
6
+ * the input recipes that name it. Nothing throws for a bad recipe or a
7
+ * recipe that does not bind: every problem is reported on its file with the
8
+ * JSON path core already computes, exactly as `opencraw validate` prints it.
9
+ *
10
+ * @param folder - The workspace folder.
11
+ * @returns Every recipe file found, each with its validation and binding issues.
12
+ * @throws Error when the folder does not exist or cannot be read.
13
+ */
14
+ export declare function openWorkspace(folder: string): Promise<WorkspaceView>;
15
+ //# sourceMappingURL=open-workspace.use-case.d.ts.map
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Writes a recipe back to its file: pretty JSON, a 2-space indent, a
3
+ * trailing newline. Phase 0 takes the JSON tab's whole parsed object rather
4
+ * than a patch, so this is a full replacement, not a splice: loading a file
5
+ * the studio has not touched and saving it again keeps every key, value and
6
+ * key order (`JSON.parse` preserves insertion order, and `JSON.stringify`
7
+ * writes it back), but not necessarily the file's exact original formatting
8
+ * (alignment, how arrays wrap, a trailing comment). Byte-identical
9
+ * round-trip for a hand-formatted file is future work; see this file's test
10
+ * for what is guaranteed today.
11
+ *
12
+ * @param path - The recipe file to write.
13
+ * @param recipe - The whole recipe object.
14
+ */
15
+ export declare function saveRecipe(path: string, recipe: unknown): Promise<void>;
16
+ //# sourceMappingURL=save-recipe.use-case.d.ts.map
@@ -0,0 +1,6 @@
1
+ export { loadRecipePair } from './load-recipe-pair.use-case.js';
2
+ export type { RecipePair } from './load-recipe-pair.use-case.js';
3
+ export { runSample } from './run-sample.use-case.js';
4
+ export type { SampleRunCallbacks, SampleRunHandle } from './run-sample.use-case.js';
5
+ export type { SampleRunRecord, SampleRunResult, SampleStoppedBy } from './sample-run-record.contract.js';
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,18 @@
1
+ import type { InputRecipe, OutputRecipe } from '@opencraw/core';
2
+ /** An input recipe and the output recipe it names, both parsed. */
3
+ export interface RecipePair {
4
+ input: InputRecipe;
5
+ output: OutputRecipe;
6
+ }
7
+ /**
8
+ * Finds and parses one input recipe of a workspace folder, and the output
9
+ * recipe it names.
10
+ *
11
+ * @param folder - The workspace folder.
12
+ * @param recipeId - The input recipe's id.
13
+ * @returns The parsed pair.
14
+ * @throws RecipeValidationError when either recipe does not parse.
15
+ * @throws Error when the input or its output is not in the folder.
16
+ */
17
+ export declare function loadRecipePair(folder: string, recipeId: string): Promise<RecipePair>;
18
+ //# sourceMappingURL=load-recipe-pair.use-case.d.ts.map
@@ -0,0 +1,30 @@
1
+ import type { SampleBudget } from '../studio-api/index.js';
2
+ import type { SampleRunRecord, SampleRunResult } from './sample-run-record.contract.js';
3
+ /** Where trace lines and records go as a sample run proceeds. */
4
+ export interface SampleRunCallbacks {
5
+ onTraceLine: (line: string) => void;
6
+ onRecord: (record: SampleRunRecord) => void;
7
+ }
8
+ /** A sample run in progress. */
9
+ export interface SampleRunHandle {
10
+ /** Closes the crawler; the run's `result` still resolves, typically with an error from the interrupted request. */
11
+ stop: () => Promise<void>;
12
+ result: Promise<SampleRunResult>;
13
+ }
14
+ /**
15
+ * Runs one input recipe of a workspace folder as a sample: `debug: true` (so
16
+ * each `record:emit` carries its data), an in-memory sink, and `budget` as
17
+ * the crawler's `sample` (`@opencraw/core`'s `CrawlOptions.sample`, #89).
18
+ * Every engine event becomes a trace line with the same `traceLine`
19
+ * formatter the cli's `--trace` uses; every emitted record reaches
20
+ * `callbacks.onRecord` as it happens, not only in the final result.
21
+ *
22
+ * @param folder - The workspace folder the recipe lives in.
23
+ * @param recipeId - The input recipe to run.
24
+ * @param budget - The sample budget; unset runs the recipe to completion (still capped by its own `limits`).
25
+ * @param callbacks - Where trace lines and records go as the run proceeds.
26
+ * @returns A handle: `stop` closes the crawler cleanly, `result` resolves when the run ends.
27
+ * @throws RecipeValidationError, RecipeBindingError, Error when the recipe or its output cannot be loaded or bound.
28
+ */
29
+ export declare function runSample(folder: string, recipeId: string, budget: SampleBudget | undefined, callbacks: SampleRunCallbacks): Promise<SampleRunHandle>;
30
+ //# sourceMappingURL=run-sample.use-case.d.ts.map
@@ -0,0 +1,19 @@
1
+ /** One record streamed or kept from a sample run. */
2
+ export interface SampleRunRecord {
3
+ key: string | null;
4
+ data: Record<string, unknown>;
5
+ }
6
+ /** A sample run's stopping reason: mirrors `@opencraw/core`'s `RecipeReport.stoppedBy`. */
7
+ export type SampleStoppedBy = 'sample-maxRecords' | 'sample-maxPages' | 'sample-maxMs';
8
+ /** What a sample run finished with. */
9
+ export interface SampleRunResult {
10
+ recipeId: string;
11
+ emitted: number;
12
+ rejected: number;
13
+ duplicates: number;
14
+ durationMs: number;
15
+ error?: string;
16
+ stoppedBy?: SampleStoppedBy;
17
+ records: SampleRunRecord[];
18
+ }
19
+ //# sourceMappingURL=sample-run-record.contract.d.ts.map
@@ -0,0 +1,67 @@
1
+ import { z } from 'zod';
2
+ /** A budget for a sample run; never authored into a recipe, only asked for by the caller. */
3
+ export declare const sampleBudgetSchema: z.ZodObject<{
4
+ maxRecords: z.ZodOptional<z.ZodNumber>;
5
+ maxPages: z.ZodOptional<z.ZodNumber>;
6
+ maxMs: z.ZodOptional<z.ZodNumber>;
7
+ }, z.core.$strip>;
8
+ /** Opens a recipe folder: the server reads it and answers with a `WorkspaceView`. */
9
+ export declare const openWorkspaceCommandSchema: z.ZodObject<{
10
+ type: z.ZodLiteral<"open-workspace">;
11
+ folder: z.ZodString;
12
+ }, z.core.$strip>;
13
+ /** Runs one input recipe under a budget, streaming `trace-line` and `record` events, ending in `run-finished`. */
14
+ export declare const runSampleCommandSchema: z.ZodObject<{
15
+ type: z.ZodLiteral<"run-sample">;
16
+ recipeId: z.ZodString;
17
+ budget: z.ZodOptional<z.ZodObject<{
18
+ maxRecords: z.ZodOptional<z.ZodNumber>;
19
+ maxPages: z.ZodOptional<z.ZodNumber>;
20
+ maxMs: z.ZodOptional<z.ZodNumber>;
21
+ }, z.core.$strip>>;
22
+ }, z.core.$strip>;
23
+ /** Closes the crawler of the run in progress, if any; a no-op otherwise. */
24
+ export declare const stopRunCommandSchema: z.ZodObject<{
25
+ type: z.ZodLiteral<"stop-run">;
26
+ }, z.core.$strip>;
27
+ /** Writes a recipe file: the whole object, pretty-printed by the server (see `recipe-workspace`). */
28
+ export declare const saveRecipeCommandSchema: z.ZodObject<{
29
+ type: z.ZodLiteral<"save-recipe">;
30
+ path: z.ZodString;
31
+ recipe: z.ZodRecord<z.ZodString, z.ZodUnknown>;
32
+ }, z.core.$strip>;
33
+ /** Fetches the HTML of an input recipe's start page, as the engine would see it. */
34
+ export declare const fetchStartPageCommandSchema: z.ZodObject<{
35
+ type: z.ZodLiteral<"fetch-start-page">;
36
+ recipeId: z.ZodString;
37
+ }, z.core.$strip>;
38
+ export declare const studioCommandSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
39
+ type: z.ZodLiteral<"open-workspace">;
40
+ folder: z.ZodString;
41
+ }, z.core.$strip>, z.ZodObject<{
42
+ type: z.ZodLiteral<"run-sample">;
43
+ recipeId: z.ZodString;
44
+ budget: z.ZodOptional<z.ZodObject<{
45
+ maxRecords: z.ZodOptional<z.ZodNumber>;
46
+ maxPages: z.ZodOptional<z.ZodNumber>;
47
+ maxMs: z.ZodOptional<z.ZodNumber>;
48
+ }, z.core.$strip>>;
49
+ }, z.core.$strip>, z.ZodObject<{
50
+ type: z.ZodLiteral<"stop-run">;
51
+ }, z.core.$strip>, z.ZodObject<{
52
+ type: z.ZodLiteral<"save-recipe">;
53
+ path: z.ZodString;
54
+ recipe: z.ZodRecord<z.ZodString, z.ZodUnknown>;
55
+ }, z.core.$strip>, z.ZodObject<{
56
+ type: z.ZodLiteral<"fetch-start-page">;
57
+ recipeId: z.ZodString;
58
+ }, z.core.$strip>], "type">;
59
+ export type SampleBudget = z.infer<typeof sampleBudgetSchema>;
60
+ export type OpenWorkspaceCommand = z.infer<typeof openWorkspaceCommandSchema>;
61
+ export type RunSampleCommand = z.infer<typeof runSampleCommandSchema>;
62
+ export type StopRunCommand = z.infer<typeof stopRunCommandSchema>;
63
+ export type SaveRecipeCommand = z.infer<typeof saveRecipeCommandSchema>;
64
+ export type FetchStartPageCommand = z.infer<typeof fetchStartPageCommandSchema>;
65
+ /** Every command the UI can send the server, over HTTP POST. */
66
+ export type StudioCommand = z.infer<typeof studioCommandSchema>;
67
+ //# sourceMappingURL=command.contract.d.ts.map
@@ -0,0 +1,70 @@
1
+ import { z } from 'zod';
2
+ /** Why a sample run stopped before finishing on its own; mirrors `@opencraw/core`'s `RecipeReport.stoppedBy`. */
3
+ export declare const stoppedBySchema: z.ZodEnum<{
4
+ "sample-maxRecords": "sample-maxRecords";
5
+ "sample-maxPages": "sample-maxPages";
6
+ "sample-maxMs": "sample-maxMs";
7
+ }>;
8
+ /** One line of the engine's own trace output (`traceLine`), streamed as a sample run proceeds. */
9
+ export declare const traceLineEventSchema: z.ZodObject<{
10
+ type: z.ZodLiteral<"trace-line">;
11
+ line: z.ZodString;
12
+ }, z.core.$strip>;
13
+ /** One record a sample run emitted. */
14
+ export declare const recordEventSchema: z.ZodObject<{
15
+ type: z.ZodLiteral<"record">;
16
+ recipeId: z.ZodString;
17
+ key: z.ZodNullable<z.ZodString>;
18
+ data: z.ZodRecord<z.ZodString, z.ZodUnknown>;
19
+ }, z.core.$strip>;
20
+ /** A sample run ended: how many records, and why it stopped when it did not finish on its own. */
21
+ export declare const runFinishedEventSchema: z.ZodObject<{
22
+ type: z.ZodLiteral<"run-finished">;
23
+ recipeId: z.ZodString;
24
+ emitted: z.ZodNumber;
25
+ rejected: z.ZodNumber;
26
+ duplicates: z.ZodNumber;
27
+ durationMs: z.ZodNumber;
28
+ error: z.ZodOptional<z.ZodString>;
29
+ stoppedBy: z.ZodOptional<z.ZodEnum<{
30
+ "sample-maxRecords": "sample-maxRecords";
31
+ "sample-maxPages": "sample-maxPages";
32
+ "sample-maxMs": "sample-maxMs";
33
+ }>>;
34
+ }, z.core.$strip>;
35
+ /** The workspace's recipes changed on disk (a save, or a future watcher) and should be re-fetched. */
36
+ export declare const workspaceChangedEventSchema: z.ZodObject<{
37
+ type: z.ZodLiteral<"workspace-changed">;
38
+ }, z.core.$strip>;
39
+ export declare const studioEventSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
40
+ type: z.ZodLiteral<"trace-line">;
41
+ line: z.ZodString;
42
+ }, z.core.$strip>, z.ZodObject<{
43
+ type: z.ZodLiteral<"record">;
44
+ recipeId: z.ZodString;
45
+ key: z.ZodNullable<z.ZodString>;
46
+ data: z.ZodRecord<z.ZodString, z.ZodUnknown>;
47
+ }, z.core.$strip>, z.ZodObject<{
48
+ type: z.ZodLiteral<"run-finished">;
49
+ recipeId: z.ZodString;
50
+ emitted: z.ZodNumber;
51
+ rejected: z.ZodNumber;
52
+ duplicates: z.ZodNumber;
53
+ durationMs: z.ZodNumber;
54
+ error: z.ZodOptional<z.ZodString>;
55
+ stoppedBy: z.ZodOptional<z.ZodEnum<{
56
+ "sample-maxRecords": "sample-maxRecords";
57
+ "sample-maxPages": "sample-maxPages";
58
+ "sample-maxMs": "sample-maxMs";
59
+ }>>;
60
+ }, z.core.$strip>, z.ZodObject<{
61
+ type: z.ZodLiteral<"workspace-changed">;
62
+ }, z.core.$strip>], "type">;
63
+ export type StoppedBy = z.infer<typeof stoppedBySchema>;
64
+ export type TraceLineEvent = z.infer<typeof traceLineEventSchema>;
65
+ export type RecordEvent = z.infer<typeof recordEventSchema>;
66
+ export type RunFinishedEvent = z.infer<typeof runFinishedEventSchema>;
67
+ export type WorkspaceChangedEvent = z.infer<typeof workspaceChangedEventSchema>;
68
+ /** Every event the server pushes to the UI over the WebSocket. */
69
+ export type StudioEvent = z.infer<typeof studioEventSchema>;
70
+ //# sourceMappingURL=event.contract.d.ts.map
@@ -0,0 +1,11 @@
1
+ export { sampleBudgetSchema, openWorkspaceCommandSchema, runSampleCommandSchema, stopRunCommandSchema, saveRecipeCommandSchema, fetchStartPageCommandSchema, studioCommandSchema } from './command.contract.js';
2
+ export type { SampleBudget, OpenWorkspaceCommand, RunSampleCommand, StopRunCommand, SaveRecipeCommand, FetchStartPageCommand, StudioCommand } from './command.contract.js';
3
+ export { stoppedBySchema, traceLineEventSchema, recordEventSchema, runFinishedEventSchema, workspaceChangedEventSchema, studioEventSchema } from './event.contract.js';
4
+ export type { StoppedBy, TraceLineEvent, RecordEvent, RunFinishedEvent, WorkspaceChangedEvent, StudioEvent } from './event.contract.js';
5
+ export { recipeIssueSchema, recipeListingSchema, workspaceViewSchema } from './workspace-view.contract.js';
6
+ export type { RecipeIssue, RecipeListing, WorkspaceView } from './workspace-view.contract.js';
7
+ export { sampleRunRecordSchema, sampleRunViewSchema } from './sample-run-view.contract.js';
8
+ export type { SampleRunRecord, SampleRunView } from './sample-run-view.contract.js';
9
+ export { startPageViewSchema } from './start-page-view.contract.js';
10
+ export type { StartPageView } from './start-page-view.contract.js';
11
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,28 @@
1
+ import { z } from 'zod';
2
+ /** One record of a sample run's result, kept for the Records tab. */
3
+ export declare const sampleRunRecordSchema: z.ZodObject<{
4
+ key: z.ZodNullable<z.ZodString>;
5
+ data: z.ZodRecord<z.ZodString, z.ZodUnknown>;
6
+ }, z.core.$strip>;
7
+ /** The last sample run's result, for a client that opens the UI after it finished. */
8
+ export declare const sampleRunViewSchema: z.ZodObject<{
9
+ recipeId: z.ZodString;
10
+ emitted: z.ZodNumber;
11
+ rejected: z.ZodNumber;
12
+ duplicates: z.ZodNumber;
13
+ durationMs: z.ZodNumber;
14
+ error: z.ZodOptional<z.ZodString>;
15
+ stoppedBy: z.ZodOptional<z.ZodEnum<{
16
+ "sample-maxRecords": "sample-maxRecords";
17
+ "sample-maxPages": "sample-maxPages";
18
+ "sample-maxMs": "sample-maxMs";
19
+ }>>;
20
+ records: z.ZodArray<z.ZodObject<{
21
+ key: z.ZodNullable<z.ZodString>;
22
+ data: z.ZodRecord<z.ZodString, z.ZodUnknown>;
23
+ }, z.core.$strip>>;
24
+ trace: z.ZodArray<z.ZodString>;
25
+ }, z.core.$strip>;
26
+ export type SampleRunRecord = z.infer<typeof sampleRunRecordSchema>;
27
+ export type SampleRunView = z.infer<typeof sampleRunViewSchema>;
28
+ //# sourceMappingURL=sample-run-view.contract.d.ts.map
@@ -0,0 +1,7 @@
1
+ import { z } from 'zod';
2
+ /** What `fetch-start-page` answers with. */
3
+ export declare const startPageViewSchema: z.ZodObject<{
4
+ html: z.ZodString;
5
+ }, z.core.$strip>;
6
+ export type StartPageView = z.infer<typeof startPageViewSchema>;
7
+ //# sourceMappingURL=start-page-view.contract.d.ts.map
@@ -0,0 +1,55 @@
1
+ import { z } from 'zod';
2
+ /** One problem found on a recipe file, with the JSON path it lives at. */
3
+ export declare const recipeIssueSchema: z.ZodObject<{
4
+ path: z.ZodString;
5
+ message: z.ZodString;
6
+ kind: z.ZodEnum<{
7
+ validation: "validation";
8
+ binding: "binding";
9
+ }>;
10
+ }, z.core.$strip>;
11
+ /** One recipe file in an opened workspace. */
12
+ export declare const recipeListingSchema: z.ZodObject<{
13
+ file: z.ZodString;
14
+ kind: z.ZodEnum<{
15
+ unknown: "unknown";
16
+ input: "input";
17
+ output: "output";
18
+ }>;
19
+ id: z.ZodOptional<z.ZodString>;
20
+ issues: z.ZodArray<z.ZodObject<{
21
+ path: z.ZodString;
22
+ message: z.ZodString;
23
+ kind: z.ZodEnum<{
24
+ validation: "validation";
25
+ binding: "binding";
26
+ }>;
27
+ }, z.core.$strip>>;
28
+ text: z.ZodString;
29
+ }, z.core.$strip>;
30
+ /** What `open-workspace` answers with: every recipe file directly inside the folder. */
31
+ export declare const workspaceViewSchema: z.ZodObject<{
32
+ folder: z.ZodString;
33
+ recipes: z.ZodArray<z.ZodObject<{
34
+ file: z.ZodString;
35
+ kind: z.ZodEnum<{
36
+ unknown: "unknown";
37
+ input: "input";
38
+ output: "output";
39
+ }>;
40
+ id: z.ZodOptional<z.ZodString>;
41
+ issues: z.ZodArray<z.ZodObject<{
42
+ path: z.ZodString;
43
+ message: z.ZodString;
44
+ kind: z.ZodEnum<{
45
+ validation: "validation";
46
+ binding: "binding";
47
+ }>;
48
+ }, z.core.$strip>>;
49
+ text: z.ZodString;
50
+ }, z.core.$strip>>;
51
+ }, z.core.$strip>;
52
+ export type RecipeIssue = z.infer<typeof recipeIssueSchema>;
53
+ export type RecipeListing = z.infer<typeof recipeListingSchema>;
54
+ export type WorkspaceView = z.infer<typeof workspaceViewSchema>;
55
+ //# sourceMappingURL=workspace-view.contract.d.ts.map
@@ -0,0 +1,26 @@
1
+ import type { IncomingMessage } from 'node:http';
2
+ /** The cookie set on the page's own document response, so its own asset and API requests carry the token too. */
3
+ export declare const TOKEN_COOKIE = "opencraw_token";
4
+ /** A random token, generated once at server startup and required on every request. */
5
+ export declare function generateToken(): string;
6
+ /**
7
+ * The token a request carries, checked in this order: the `x-opencraw-token`
8
+ * header, the `token` query param, the studio's own cookie (set after the
9
+ * first authenticated document request, so a browser's own asset and API
10
+ * requests need not repeat the query param).
11
+ *
12
+ * @param request - The incoming request.
13
+ * @param query - Its URL's query string, parsed.
14
+ * @returns The token found, or `undefined`.
15
+ */
16
+ export declare function tokenOf(request: Pick<IncomingMessage, 'headers'>, query: URLSearchParams): string | undefined;
17
+ /**
18
+ * Whether a request proves it holds the server's token.
19
+ *
20
+ * @param expected - The server's token.
21
+ * @param request - The incoming request.
22
+ * @param query - Its URL's query string, parsed.
23
+ * @returns Whether the request is authorized.
24
+ */
25
+ export declare function isAuthorized(expected: string, request: Pick<IncomingMessage, 'headers'>, query: URLSearchParams): boolean;
26
+ //# sourceMappingURL=access-token.policy.d.ts.map
@@ -0,0 +1,14 @@
1
+ import type { FetchStartPageCommand, StartPageView } from '../studio-api/index.js';
2
+ import type { StudioState } from './workspace.store.js';
3
+ /**
4
+ * Handles `fetch-start-page`: loads the named input recipe from the current
5
+ * workspace and fetches its start point (see `page-snapshot`'s own caveats:
6
+ * no session bootstrap, a fresh browser or HTTP context per call).
7
+ *
8
+ * @param state - The server state: `folder` must already be set.
9
+ * @param command - The command.
10
+ * @returns The page's content as plain text.
11
+ * @throws Error when no workspace is open, or the recipe cannot be found or loaded.
12
+ */
13
+ export declare function handleFetchStartPage(state: StudioState, command: FetchStartPageCommand): Promise<StartPageView>;
14
+ //# sourceMappingURL=fetch-start-page.handler.d.ts.map
@@ -0,0 +1,3 @@
1
+ export { startStudioServer } from './studio-http.use-case.js';
2
+ export type { StudioServer, StudioServerOptions } from './studio-http.use-case.js';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,13 @@
1
+ import type { OpenWorkspaceCommand, WorkspaceView } from '../studio-api/index.js';
2
+ import type { StudioState } from './workspace.store.js';
3
+ /**
4
+ * Handles `open-workspace`: reads the folder, remembers it as the server's
5
+ * current workspace (every later command that names a recipe resolves it
6
+ * against this folder), and tells every connected client the workspace changed.
7
+ *
8
+ * @param state - The server state to update.
9
+ * @param command - The command.
10
+ * @returns The workspace's recipes and their issues.
11
+ */
12
+ export declare function handleOpenWorkspace(state: StudioState, command: OpenWorkspaceCommand): Promise<WorkspaceView>;
13
+ //# sourceMappingURL=open-workspace.handler.d.ts.map
@@ -0,0 +1,28 @@
1
+ import type { RunSampleCommand, StopRunCommand } from '../studio-api/index.js';
2
+ import type { StudioState } from './workspace.store.js';
3
+ /**
4
+ * Handles `run-sample`: stops whatever sample was still running (one run at
5
+ * a time), starts the new one, and returns once it has started, not once it
6
+ * finishes: `trace-line` and `record` stream over the WebSocket as the run
7
+ * proceeds, and `run-finished` closes it out.
8
+ *
9
+ * @param state - The server state: `folder` must already be set (`open-workspace` first).
10
+ * @param command - The command.
11
+ * @returns `{ started: true }` once the run has begun.
12
+ * @throws Error when no workspace is open, or the recipe cannot be loaded or bound.
13
+ */
14
+ export declare function handleRunSample(state: StudioState, command: RunSampleCommand): Promise<{
15
+ started: true;
16
+ }>;
17
+ /**
18
+ * Handles `stop-run`: closes the crawler of the run in progress, if any.
19
+ * `run-finished` still follows once the interrupted run's promise settles.
20
+ *
21
+ * @param state - The server state holding the active run, if any.
22
+ * @param _command - The command (carries nothing beyond its type).
23
+ * @returns Whether a run was actually in progress to stop.
24
+ */
25
+ export declare function handleStopRun(state: StudioState, _command: StopRunCommand): Promise<{
26
+ stopped: boolean;
27
+ }>;
28
+ //# sourceMappingURL=run-sample.handler.d.ts.map
@@ -0,0 +1,15 @@
1
+ import type { SaveRecipeCommand } from '../studio-api/index.js';
2
+ import type { StudioState } from './workspace.store.js';
3
+ /**
4
+ * Handles `save-recipe`: writes the file (see `recipe-workspace/save-recipe.use-case.ts`
5
+ * for the pretty-printing and its round-trip guarantee), then tells every
6
+ * connected client the workspace changed, so the UI can re-fetch it.
7
+ *
8
+ * @param state - The server state holding the connected sockets.
9
+ * @param command - The command: the file path and the whole recipe object.
10
+ * @returns `{ saved: true }` once the write has completed.
11
+ */
12
+ export declare function handleSaveRecipe(state: StudioState, command: SaveRecipeCommand): Promise<{
13
+ saved: true;
14
+ }>;
15
+ //# sourceMappingURL=save-recipe.handler.d.ts.map
@@ -0,0 +1,14 @@
1
+ import type { ServerResponse } from 'node:http';
2
+ /**
3
+ * Serves the built UI from `root` (`packages/studio/dist/ui`, `apps/studio-ui`'s
4
+ * build output): the requested path if it exists as a file, `index.html`
5
+ * otherwise, so a client-side route (or the bare `/`) always gets the SPA's
6
+ * shell. `root` not existing at all (the UI has not been built) answers 404
7
+ * with a hint rather than a stack trace.
8
+ *
9
+ * @param root - The built UI's folder.
10
+ * @param pathname - The request's URL path.
11
+ * @param response - The response to write to.
12
+ */
13
+ export declare function serveStatic(root: string, pathname: string, response: ServerResponse): void;
14
+ //# sourceMappingURL=static-file.handler.d.ts.map
@@ -0,0 +1,32 @@
1
+ export interface StudioServerOptions {
2
+ /** Where the built UI lives; default: `dist/ui` next to the running server code. */
3
+ uiRoot?: string;
4
+ /** A fixed port; default: an OS-assigned free port. */
5
+ port?: number;
6
+ /**
7
+ * The folder `opencraw-studio` was started with, if any: set as the
8
+ * server's current workspace right away, so `run-sample`, `save-recipe`
9
+ * and `fetch-start-page` work immediately, before the UI's own
10
+ * `open-workspace` call (which it still makes, to get the recipe listing;
11
+ * the folder reaches it through the URL's `folder` query param).
12
+ */
13
+ initialFolder?: string;
14
+ }
15
+ /** The running server: its URL (token included, ready to open), the token alone, and how to stop it. */
16
+ export interface StudioServer {
17
+ url: string;
18
+ token: string;
19
+ close: () => Promise<void>;
20
+ }
21
+ /**
22
+ * Starts the studio's HTTP server: one `POST /api/command` for every
23
+ * command `studio-api` defines, `GET /ws` for the event stream, and the
24
+ * built UI for everything else. Binds `127.0.0.1` only; every request needs
25
+ * the token (`access-token.policy.ts`) — as a header, a query param, or the
26
+ * cookie the server sets once a document request proves the query param.
27
+ *
28
+ * @param options - The UI root and the port; both default sensibly.
29
+ * @returns The running server.
30
+ */
31
+ export declare function startStudioServer(options?: StudioServerOptions): Promise<StudioServer>;
32
+ //# sourceMappingURL=studio-http.use-case.d.ts.map
@@ -0,0 +1,22 @@
1
+ import type { IncomingMessage } from 'node:http';
2
+ import type { Duplex } from 'node:stream';
3
+ /** One accepted WebSocket connection: text frames only, no compression, no fragmentation. */
4
+ export interface WsConnection {
5
+ send: (text: string) => void;
6
+ close: () => void;
7
+ onMessage: (handler: (text: string) => void) => void;
8
+ onClose: (handler: () => void) => void;
9
+ }
10
+ /**
11
+ * Accepts one WebSocket upgrade on the raw socket `node:http` hands an
12
+ * `upgrade` listener, implementing just enough of RFC 6455 for the studio's
13
+ * own one-directional event stream: the server sends small JSON text
14
+ * frames, the client sends none (commands go over `POST /api/command`). No
15
+ * `ws` dependency: this is deliberately small rather than general-purpose.
16
+ *
17
+ * @param request - The upgrade request (its `Sec-WebSocket-Key` header).
18
+ * @param socket - The raw, already-connected socket.
19
+ * @returns The connection: `send`, `close`, and where messages and the close go.
20
+ */
21
+ export declare function acceptWebSocket(request: IncomingMessage, socket: Duplex): WsConnection;
22
+ //# sourceMappingURL=websocket.client.d.ts.map
@@ -0,0 +1,21 @@
1
+ import type { SampleRunHandle } from '../sample-run/index.js';
2
+ import type { StudioEvent } from '../studio-api/index.js';
3
+ import type { WsConnection } from './websocket.client.js';
4
+ /** The server's mutable state: the opened folder, the run in progress (if any), and the connected event sockets. */
5
+ export interface StudioState {
6
+ folder?: string;
7
+ activeRun?: SampleRunHandle;
8
+ sockets: Set<WsConnection>;
9
+ }
10
+ /** @returns Fresh state for a new server instance: no workspace open, no run, no sockets. */
11
+ export declare function createStudioState(): StudioState;
12
+ /**
13
+ * Sends one event to every connected client. A socket that errors while
14
+ * sending (gone away without a clean close yet reaching us) is dropped
15
+ * rather than left to fail every later broadcast too.
16
+ *
17
+ * @param state - The server state holding the connected sockets.
18
+ * @param event - The event to send.
19
+ */
20
+ export declare function broadcast(state: StudioState, event: StudioEvent): void;
21
+ //# sourceMappingURL=workspace.store.d.ts.map