@opencraw/studio 0.0.11 → 0.0.13

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 (2) hide show
  1. package/dist/index.esm.js +37 -13
  2. package/package.json +3 -3
package/dist/index.esm.js CHANGED
@@ -1,12 +1,12 @@
1
1
  import { join, dirname, basename, extname, normalize, sep, resolve } from 'node:path';
2
2
  import { createServer } from 'node:http';
3
+ import { promisify, stripVTControlCharacters } from 'node:util';
3
4
  import { fileURLToPath, pathToFileURL } from 'node:url';
4
5
  import { z } from 'zod';
5
- import { BODY_KINDS, HttpClient, BrowserClient, NODE_ID_ATTRIBUTE, rowsOfCells, parseXml, findDeckTables, findGridTables, analyzeTables, parseRegion, regionText, deckRegionText, isDeckDocument, readRecipeSource, parseInputRecipe, parseOutputRecipe, bindRecipeSet, memorySink, createCrawler, traceLine, UnknownHookError, resolveMissingPolicy, isWorkbookDocument, RecipeBindingError, RecipeValidationError, countMatches, matchesOf, tryParseJson, isPdfDocument, BrowserProfiles } from '@opencraw/core';
6
+ import { BODY_KINDS, HttpClient, BrowserClient, NODE_ID_ATTRIBUTE, sessionStateOf, rowsOfCells, parseXml, findDeckTables, findGridTables, analyzeTables, parseRegion, regionText, deckRegionText, isDeckDocument, readRecipeSource, parseInputRecipe, parseOutputRecipe, bindRecipeSet, memorySink, createCrawler, traceLine, UnknownHookError, resolveMissingPolicy, isWorkbookDocument, RecipeBindingError, RecipeValidationError, countMatches, matchesOf, tryParseJson, isPdfDocument, BrowserProfiles } from '@opencraw/core';
6
7
  import { randomBytes, createHash } from 'node:crypto';
7
8
  import { execFile } from 'node:child_process';
8
9
  import { createRequire } from 'node:module';
9
- import { promisify } from 'node:util';
10
10
  import { load } from 'cheerio';
11
11
  import { readFile, writeFile, mkdir } from 'node:fs/promises';
12
12
  import { describeJson } from '@opencraw/probe';
@@ -1119,12 +1119,30 @@ async function takeSnapshot(input, stepPath, browser) {
1119
1119
  if (stepPath !== 'start') throw new Error(`takeSnapshot: "${stepPath}" is not supported yet — only "start" (the first start point, before any step runs) is; see this function's doc comment`);
1120
1120
  const point = input.start[0];
1121
1121
  if (point === undefined) throw new Error(`recipe "${input.id}" has no start point`);
1122
- return input.mode === 'web' ? snapshotWeb(point.url, browser) : snapshotApi(point.url);
1122
+ const storageState = await loginState(input, browser);
1123
+ return input.mode === 'web' ? snapshotWeb(point.url, storageState, browser) : snapshotApi(point.url, storageState);
1123
1124
  }
1124
- async function snapshotWeb(url, browser) {
1125
+ /** What the recipe's own login leaves behind, so the snapshot shows the page as the crawl sees it (issue #185); nothing when it declares no login. */
1126
+ async function loginState(input, browser) {
1127
+ if (input.session === undefined) return undefined;
1128
+ let client;
1129
+ try {
1130
+ return await sessionStateOf(input, {
1131
+ browser: async () => {
1132
+ client = await ensureBrowserLaunch(browser, () => BrowserClient.launch(browser));
1133
+ return client;
1134
+ }
1135
+ });
1136
+ } finally {
1137
+ await client?.close();
1138
+ }
1139
+ }
1140
+ async function snapshotWeb(url, storageState, browser) {
1125
1141
  const client = await ensureBrowserLaunch(browser, () => BrowserClient.launch(browser));
1126
1142
  try {
1127
- const session = await client.newSession();
1143
+ const session = await client.newSession({
1144
+ storageState
1145
+ });
1128
1146
  try {
1129
1147
  await session.page.goto(url);
1130
1148
  await session.page.evaluate(hiddenMarksScript(HIDDEN_ATTRIBUTE));
@@ -1143,8 +1161,10 @@ async function snapshotWeb(url, browser) {
1143
1161
  await client.close();
1144
1162
  }
1145
1163
  }
1146
- async function snapshotApi(url) {
1147
- const client = await HttpClient.open({});
1164
+ async function snapshotApi(url, storageState) {
1165
+ const client = await HttpClient.open({
1166
+ storageState
1167
+ });
1148
1168
  try {
1149
1169
  const response = await client.send({
1150
1170
  url
@@ -2105,9 +2125,9 @@ async function runToResult(crawler, set, recipeId, records, rejectedRecords, ste
2105
2125
  }
2106
2126
  /** What a run that could not start reports. A hook is the author's own code, which Studio never loads (issue #150): say so, instead of leaving "unknown hook" to read as a typo. */
2107
2127
  function failureMessage(error) {
2108
- if (error instanceof UnknownHookError) return `${error.message}. Studio does not load hooks, so a recipe that uses one cannot be sample-run here; run it with the cli or the mcp server, or temporarily replace the hook step to preview the rest.`;
2109
- if (error instanceof Error) return error.message;
2110
- return String(error);
2128
+ if (error instanceof UnknownHookError) return `${stripVTControlCharacters(error.message)}. Studio does not load hooks, so a recipe that uses one cannot be sample-run here; run it with the cli or the mcp server, or temporarily replace the hook step to preview the rest.`;
2129
+ if (error instanceof Error) return stripVTControlCharacters(error.message);
2130
+ return stripVTControlCharacters(String(error));
2111
2131
  }
2112
2132
 
2113
2133
  /** @returns A fresh, empty cache. */
@@ -5070,7 +5090,7 @@ async function handlePdfBytesRequest(response, state, query) {
5070
5090
  response.end(Buffer.from(bytes));
5071
5091
  } catch (error) {
5072
5092
  respondJson(response, 500, {
5073
- error: error instanceof Error ? error.message : String(error)
5093
+ error: messageOf(error)
5074
5094
  });
5075
5095
  }
5076
5096
  }
@@ -5088,7 +5108,7 @@ async function handleCommand(request, response, state) {
5088
5108
  command = parsed.data;
5089
5109
  } catch (error) {
5090
5110
  respondJson(response, 400, {
5091
- error: error instanceof Error ? error.message : String(error)
5111
+ error: messageOf(error)
5092
5112
  });
5093
5113
  return;
5094
5114
  }
@@ -5096,7 +5116,7 @@ async function handleCommand(request, response, state) {
5096
5116
  respondJson(response, 200, await dispatch(command, state));
5097
5117
  } catch (error) {
5098
5118
  respondJson(response, 500, {
5099
- error: error instanceof Error ? error.message : String(error)
5119
+ error: messageOf(error)
5100
5120
  });
5101
5121
  }
5102
5122
  }
@@ -5226,6 +5246,10 @@ function handleUpgrade(request, socket, token, state) {
5226
5246
  state.sockets.delete(connection);
5227
5247
  });
5228
5248
  }
5249
+ /** An error's message as plain text: Playwright colours its call logs with ANSI codes, which a browser would show as raw escapes (issue #187). */
5250
+ function messageOf(error) {
5251
+ return stripVTControlCharacters(error instanceof Error ? error.message : String(error));
5252
+ }
5229
5253
 
5230
5254
  /**
5231
5255
  * Runs `opencraw-studio`: starts the server, opens `folder` (if given) as
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opencraw/studio",
3
- "version": "0.0.11",
3
+ "version": "0.0.13",
4
4
  "type": "module",
5
5
  "main": "./dist/index.esm.js",
6
6
  "module": "./dist/index.esm.js",
@@ -24,8 +24,8 @@
24
24
  "!**/*.js.map"
25
25
  ],
26
26
  "dependencies": {
27
- "@opencraw/core": "^0.1.22",
28
- "@opencraw/probe": "^0.0.5",
27
+ "@opencraw/core": "^0.1.24",
28
+ "@opencraw/probe": "^0.0.7",
29
29
  "cheerio": "^1.2.0",
30
30
  "playwright": "^1.63.0",
31
31
  "zod": "^4.6.5",