autokap 1.0.6 → 1.0.7

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 (130) hide show
  1. package/assets/chrome/ios-statusbar-comparison-reference.jpg +0 -0
  2. package/assets/chrome/ios-statusbar-dark-reference.jpg +0 -0
  3. package/assets/chrome/ios-statusbar-light-reference.jpg +0 -0
  4. package/assets/devices/ipad-pro-11-m4.json +52 -0
  5. package/assets/devices/iphone-16-pro.json +53 -0
  6. package/assets/devices/macbook-air-13.json +45 -0
  7. package/assets/frames/MacBook Air 13.svg +242 -0
  8. package/assets/frames/Status bar - iPhone.png +0 -0
  9. Menu bar- iPad.png +0 -0
  10. package/assets/frames/iPad Pro M4 11_.png +0 -0
  11. package/assets/frames/iPhone 16 Pro.png +0 -0
  12. package/assets/icons/Cellular Connection.svg +3 -0
  13. package/assets/icons/Union.svg +6 -0
  14. package/assets/icons/Wifi.svg +3 -0
  15. package/assets/icons/battery.svg +5 -0
  16. package/assets/icons/battery_charging.svg +8 -0
  17. package/dist/abort.d.ts +5 -0
  18. package/dist/abort.js +44 -0
  19. package/dist/agent.d.ts +142 -0
  20. package/dist/agent.js +4511 -0
  21. package/dist/billing-operation-logging.d.ts +38 -0
  22. package/dist/billing-operation-logging.js +248 -0
  23. package/dist/browser-bar.d.ts +40 -0
  24. package/dist/browser-bar.js +147 -0
  25. package/dist/browser.d.ts +25 -0
  26. package/dist/browser.js +177 -9
  27. package/dist/capture-alt-text.d.ts +12 -0
  28. package/dist/capture-alt-text.js +51 -0
  29. package/dist/capture-encryption.d.ts +10 -0
  30. package/dist/capture-encryption.js +41 -0
  31. package/dist/capture-language-preflight.d.ts +41 -0
  32. package/dist/capture-language-preflight.js +286 -0
  33. package/dist/capture-llm-page-identity.d.ts +15 -0
  34. package/dist/capture-llm-page-identity.js +116 -0
  35. package/dist/capture-model-resolution.d.ts +9 -0
  36. package/dist/capture-model-resolution.js +21 -0
  37. package/dist/capture-page-identity.d.ts +9 -0
  38. package/dist/capture-page-identity.js +219 -0
  39. package/dist/capture-preset-credentials.d.ts +12 -0
  40. package/dist/capture-preset-credentials.js +57 -0
  41. package/dist/capture-request-plan.d.ts +58 -0
  42. package/dist/capture-request-plan.js +216 -0
  43. package/dist/capture-run-optimizer.d.ts +139 -0
  44. package/dist/capture-run-optimizer.js +848 -0
  45. package/dist/capture-selector-memory.d.ts +26 -0
  46. package/dist/capture-selector-memory.js +327 -0
  47. package/dist/capture-session-profile-encryption.d.ts +2 -0
  48. package/dist/capture-session-profile-encryption.js +22 -0
  49. package/dist/capture-step-timeout.d.ts +10 -0
  50. package/dist/capture-step-timeout.js +30 -0
  51. package/dist/capture-studio-sync.d.ts +22 -0
  52. package/dist/capture-studio-sync.js +166 -0
  53. package/dist/capture-variant-state.d.ts +54 -0
  54. package/dist/capture-variant-state.js +156 -0
  55. package/dist/cli.js +15 -0
  56. package/dist/clip-orchestrator.d.ts +148 -0
  57. package/dist/clip-orchestrator.js +950 -0
  58. package/dist/clip-postprocess.d.ts +42 -0
  59. package/dist/clip-postprocess.js +192 -0
  60. package/dist/cost-logging.d.ts +27 -0
  61. package/dist/cost-logging.js +128 -0
  62. package/dist/credential-templates.d.ts +5 -0
  63. package/dist/credential-templates.js +60 -0
  64. package/dist/element-capture.d.ts +53 -0
  65. package/dist/element-capture.js +766 -0
  66. package/dist/hybrid-navigator.d.ts +138 -0
  67. package/dist/hybrid-navigator.js +468 -0
  68. package/dist/index.d.ts +15 -0
  69. package/dist/index.js +11 -0
  70. package/dist/llm-usage.d.ts +17 -0
  71. package/dist/llm-usage.js +45 -0
  72. package/dist/mockup-html.d.ts +119 -0
  73. package/dist/mockup-html.js +253 -0
  74. package/dist/mockup.d.ts +94 -0
  75. package/dist/mockup.js +608 -0
  76. package/dist/mouse-animation.d.ts +46 -0
  77. package/dist/mouse-animation.js +100 -0
  78. package/dist/overlay-utils.d.ts +14 -0
  79. package/dist/overlay-utils.js +13 -0
  80. package/dist/posthog.d.ts +4 -0
  81. package/dist/posthog.js +26 -0
  82. package/dist/prompt-cache.d.ts +10 -0
  83. package/dist/prompt-cache.js +24 -0
  84. package/dist/prompts.d.ts +167 -0
  85. package/dist/prompts.js +1165 -0
  86. package/dist/remote-browser.d.ts +191 -0
  87. package/dist/remote-browser.js +305 -0
  88. package/dist/security.d.ts +20 -0
  89. package/dist/security.js +569 -0
  90. package/dist/server-capture-runtime.d.ts +123 -0
  91. package/dist/server-capture-runtime.js +638 -0
  92. package/dist/server-credit-usage.d.ts +12 -0
  93. package/dist/server-credit-usage.js +41 -0
  94. package/dist/server-posthog.d.ts +2 -0
  95. package/dist/server-posthog.js +16 -0
  96. package/dist/server-project-webhooks.d.ts +45 -0
  97. package/dist/server-project-webhooks.js +97 -0
  98. package/dist/server-screenshot-watermark.d.ts +7 -0
  99. package/dist/server-screenshot-watermark.js +38 -0
  100. package/dist/session-profile.d.ts +86 -0
  101. package/dist/session-profile.js +1373 -0
  102. package/dist/sf-pro-fonts.d.ts +4 -0
  103. package/dist/sf-pro-fonts.js +7 -0
  104. package/dist/status-bar-l10n.d.ts +14 -0
  105. package/dist/status-bar-l10n.js +177 -0
  106. package/dist/status-bar.d.ts +44 -0
  107. package/dist/status-bar.js +336 -0
  108. package/dist/tools.d.ts +4 -0
  109. package/dist/tools.js +578 -0
  110. package/dist/video-agent.d.ts +143 -0
  111. package/dist/video-agent.js +4783 -0
  112. package/dist/video-observation.d.ts +36 -0
  113. package/dist/video-observation.js +192 -0
  114. package/dist/video-planner.d.ts +12 -0
  115. package/dist/video-planner.js +500 -0
  116. package/dist/video-prompts.d.ts +37 -0
  117. package/dist/video-prompts.js +554 -0
  118. package/dist/video-tools.d.ts +3 -0
  119. package/dist/video-tools.js +59 -0
  120. package/dist/video-variant-state.d.ts +29 -0
  121. package/dist/video-variant-state.js +80 -0
  122. package/dist/vision-model.d.ts +17 -0
  123. package/dist/vision-model.js +74 -0
  124. package/dist/ws-auth.d.ts +20 -0
  125. package/dist/ws-auth.js +67 -0
  126. package/dist/ws-handler.d.ts +10 -0
  127. package/dist/ws-handler.js +1663 -0
  128. package/dist/ws-server.d.ts +9 -0
  129. package/dist/ws-server.js +52 -0
  130. package/package.json +93 -39
@@ -0,0 +1,26 @@
1
+ import type { SupabaseClient } from '@supabase/supabase-js';
2
+ export interface ScreenshotSelectorMemoryUpdate {
3
+ stepSignature: string;
4
+ selector: string;
5
+ source: 'agent' | 'deterministic' | 'manual';
6
+ success: boolean;
7
+ }
8
+ export declare function loadScreenshotSelectorMemory(supabase: SupabaseClient, params: {
9
+ projectId: string | null;
10
+ presetId?: string | null;
11
+ domain: string;
12
+ lang: string;
13
+ theme: 'light' | 'dark';
14
+ }): Promise<Record<string, string[]>>;
15
+ export declare function persistScreenshotSelectorMemoryUpdates(supabase: SupabaseClient, params: {
16
+ projectId: string | null;
17
+ presetId?: string | null;
18
+ domain: string;
19
+ lang: string;
20
+ theme: 'light' | 'dark';
21
+ }, updates: ScreenshotSelectorMemoryUpdate[]): Promise<void>;
22
+ export declare function extractSelectorUpdates(actions: Array<{
23
+ action: string;
24
+ params: Record<string, unknown>;
25
+ success: boolean;
26
+ }>): ScreenshotSelectorMemoryUpdate[];
@@ -0,0 +1,327 @@
1
+ const GENERIC_NAV_TOKENS = new Set([
2
+ 'page',
3
+ 'pages',
4
+ 'open',
5
+ 'view',
6
+ 'link',
7
+ 'button',
8
+ 'click',
9
+ 'menu',
10
+ 'nav',
11
+ 'navigation',
12
+ 'section',
13
+ 'screen',
14
+ 'capture',
15
+ 'prepare',
16
+ 'show',
17
+ 'the',
18
+ 'and',
19
+ 'for',
20
+ 'with',
21
+ ]);
22
+ const INTERNAL_AUTOMATION_SELECTOR_RE = /\[data-ak-[^\]]+\]|data-ak-interactive-index/i;
23
+ function containsInternalAutomationSelector(selector) {
24
+ return !!selector && INTERNAL_AUTOMATION_SELECTOR_RE.test(selector);
25
+ }
26
+ function uniqueInOrder(values) {
27
+ const seen = new Set();
28
+ const result = [];
29
+ for (const value of values) {
30
+ if (seen.has(value))
31
+ continue;
32
+ seen.add(value);
33
+ result.push(value);
34
+ }
35
+ return result;
36
+ }
37
+ function normalizeNavigationTokens(values) {
38
+ const tokens = [];
39
+ for (const value of values) {
40
+ if (!value)
41
+ continue;
42
+ const parts = value
43
+ .toLowerCase()
44
+ .split(/[^a-z0-9]+/i)
45
+ .map((part) => part.trim())
46
+ .filter((part) => part.length >= 3 && part.length <= 32 && !GENERIC_NAV_TOKENS.has(part));
47
+ tokens.push(...parts);
48
+ }
49
+ return uniqueInOrder(tokens).slice(0, 8);
50
+ }
51
+ function extractPathTokens(rawUrl) {
52
+ if (!rawUrl)
53
+ return [];
54
+ try {
55
+ const url = new URL(rawUrl);
56
+ return normalizeNavigationTokens(url.pathname.split('/'));
57
+ }
58
+ catch {
59
+ return normalizeNavigationTokens(rawUrl.split('/'));
60
+ }
61
+ }
62
+ export async function loadScreenshotSelectorMemory(supabase, params) {
63
+ const selectFields = 'step_signature, selector, confidence, last_success_at';
64
+ const grouped = {};
65
+ const appendRows = (rows) => {
66
+ for (const row of rows) {
67
+ const signature = String(row.step_signature ?? '').trim();
68
+ const selector = String(row.selector ?? '').trim();
69
+ if (!signature || !selector)
70
+ continue;
71
+ if (!grouped[signature])
72
+ grouped[signature] = [];
73
+ if (!grouped[signature].includes(selector)) {
74
+ grouped[signature].push(selector);
75
+ }
76
+ }
77
+ };
78
+ if (params.presetId) {
79
+ let query = supabase
80
+ .from('screenshot_step_memory')
81
+ .select(selectFields)
82
+ .eq('domain', params.domain)
83
+ .eq('preset_id', params.presetId)
84
+ .eq('lang', params.lang)
85
+ .eq('theme', params.theme)
86
+ .order('confidence', { ascending: false })
87
+ .order('last_success_at', { ascending: false })
88
+ .limit(200);
89
+ if (params.projectId) {
90
+ query = query.eq('project_id', params.projectId);
91
+ }
92
+ const { data } = await query;
93
+ if (data && data.length > 0) {
94
+ appendRows(data);
95
+ return grouped;
96
+ }
97
+ }
98
+ let fallbackQuery = supabase
99
+ .from('screenshot_step_memory')
100
+ .select(selectFields)
101
+ .eq('domain', params.domain)
102
+ .order('confidence', { ascending: false })
103
+ .order('last_success_at', { ascending: false })
104
+ .limit(200);
105
+ if (params.projectId) {
106
+ fallbackQuery = fallbackQuery.eq('project_id', params.projectId);
107
+ }
108
+ const { data: fallbackData } = await fallbackQuery;
109
+ if (fallbackData) {
110
+ appendRows(fallbackData);
111
+ }
112
+ return grouped;
113
+ }
114
+ export async function persistScreenshotSelectorMemoryUpdates(supabase, params, updates) {
115
+ const nowIso = new Date().toISOString();
116
+ const uniqueUpdates = new Map();
117
+ for (const update of updates) {
118
+ if (!update.stepSignature || !update.selector)
119
+ continue;
120
+ const key = `${update.stepSignature}|${update.selector}|${update.source}`;
121
+ uniqueUpdates.set(key, update);
122
+ }
123
+ for (const update of uniqueUpdates.values()) {
124
+ const baseQuery = supabase
125
+ .from('screenshot_step_memory')
126
+ .select('id, success_count, fail_count, confidence')
127
+ .eq('domain', params.domain)
128
+ .eq('step_signature', update.stepSignature)
129
+ .eq('selector', update.selector)
130
+ .eq('lang', params.lang)
131
+ .eq('theme', params.theme)
132
+ .limit(1);
133
+ const scopedQuery = params.presetId
134
+ ? baseQuery.eq('preset_id', params.presetId)
135
+ : baseQuery.is('preset_id', null);
136
+ const projectScopedQuery = params.projectId
137
+ ? scopedQuery.eq('project_id', params.projectId)
138
+ : scopedQuery.is('project_id', null);
139
+ const { data: existingRows } = await projectScopedQuery;
140
+ const existing = existingRows?.[0];
141
+ if (!existing) {
142
+ await supabase.from('screenshot_step_memory').insert({
143
+ project_id: params.projectId,
144
+ preset_id: params.presetId ?? null,
145
+ domain: params.domain,
146
+ lang: params.lang,
147
+ theme: params.theme,
148
+ step_signature: update.stepSignature,
149
+ selector: update.selector,
150
+ source: update.source,
151
+ success_count: update.success ? 1 : 0,
152
+ fail_count: update.success ? 0 : 1,
153
+ confidence: update.success ? 0.65 : 0.35,
154
+ last_success_at: update.success ? nowIso : null,
155
+ last_failure_at: update.success ? null : nowIso,
156
+ });
157
+ continue;
158
+ }
159
+ const successCount = (existing.success_count ?? 0) + (update.success ? 1 : 0);
160
+ const failCount = (existing.fail_count ?? 0) + (update.success ? 0 : 1);
161
+ const currentConfidence = existing.confidence ?? 0.5;
162
+ const confidence = update.success
163
+ ? Math.min(0.98, currentConfidence + 0.08)
164
+ : Math.max(0.05, currentConfidence - 0.12);
165
+ await supabase
166
+ .from('screenshot_step_memory')
167
+ .update({
168
+ success_count: successCount,
169
+ fail_count: failCount,
170
+ confidence,
171
+ last_success_at: update.success ? nowIso : undefined,
172
+ last_failure_at: update.success ? undefined : nowIso,
173
+ })
174
+ .eq('id', existing.id);
175
+ }
176
+ }
177
+ export function extractSelectorUpdates(actions) {
178
+ const updates = [];
179
+ const languageAliases = {
180
+ en: ['en', 'english'],
181
+ fr: ['fr', 'french', 'francais', 'français'],
182
+ de: ['de', 'german', 'deutsch'],
183
+ es: ['es', 'spanish', 'espanol', 'español'],
184
+ it: ['it', 'italian', 'italiano'],
185
+ pt: ['pt', 'portuguese', 'portugues', 'português'],
186
+ };
187
+ for (const action of actions) {
188
+ if (action.action !== 'click'
189
+ && action.action !== 'type_text'
190
+ && action.action !== 'safe_expand'
191
+ && action.action !== 'select_option') {
192
+ continue;
193
+ }
194
+ const selector = action.params.selector
195
+ ?? action.params.resolvedSelector
196
+ ?? (action.params.index !== undefined ? `[index:${action.params.index}]` : null);
197
+ if (!selector || selector.startsWith('[index:') || containsInternalAutomationSelector(selector)) {
198
+ continue;
199
+ }
200
+ const text = String(action.params.text ?? '').toLowerCase();
201
+ const label = String(action.params.elementLabel ?? '').toLowerCase();
202
+ const optionLabel = String(action.params.optionLabel ?? '').toLowerCase();
203
+ const optionValue = String(action.params.optionValue ?? '').toLowerCase();
204
+ const href = String(action.params.href ?? '').toLowerCase();
205
+ const selectorLower = selector.toLowerCase();
206
+ const combined = `${selectorLower} ${text} ${label} ${optionLabel} ${optionValue} ${href}`;
207
+ if (combined.includes('login')
208
+ || combined.includes('sign in')
209
+ || combined.includes('signin')
210
+ || combined.includes('password')
211
+ || combined.includes('email')
212
+ || combined.includes('auth')) {
213
+ updates.push({
214
+ stepSignature: action.action === 'type_text'
215
+ ? combined.includes('password')
216
+ ? 'login:password_field'
217
+ : 'login:email_field'
218
+ : 'login:submit',
219
+ selector,
220
+ source: 'agent',
221
+ success: action.success,
222
+ });
223
+ }
224
+ if (action.action === 'type_text'
225
+ && (combined.includes('search') || combined.includes('query'))) {
226
+ updates.push({
227
+ stepSignature: 'search:field',
228
+ selector,
229
+ source: 'agent',
230
+ success: action.success,
231
+ });
232
+ }
233
+ if ((action.action === 'click' || action.action === 'safe_expand')
234
+ && (combined.includes('menu') || combined.includes('navigation') || combined.includes('hamburger') || combined.includes('drawer'))) {
235
+ updates.push({
236
+ stepSignature: 'menu:primary',
237
+ selector,
238
+ source: 'agent',
239
+ success: action.success,
240
+ });
241
+ }
242
+ if ((action.action === 'click' || action.action === 'safe_expand')
243
+ && (combined.includes('account') || combined.includes('profile') || combined.includes('avatar') || combined.includes('user'))) {
244
+ updates.push({
245
+ stepSignature: 'account:menu',
246
+ selector,
247
+ source: 'agent',
248
+ success: action.success,
249
+ });
250
+ }
251
+ const languageLike = combined.includes('lang')
252
+ || combined.includes('locale')
253
+ || combined.includes('language')
254
+ || Object.values(languageAliases).some((aliases) => aliases.some((alias) => combined.includes(alias)));
255
+ if (languageLike) {
256
+ updates.push({
257
+ stepSignature: 'lang:switcher',
258
+ selector,
259
+ source: 'agent',
260
+ success: action.success,
261
+ });
262
+ for (const [langCode, aliases] of Object.entries(languageAliases)) {
263
+ if (aliases.some((alias) => combined.includes(alias))) {
264
+ updates.push({
265
+ stepSignature: `lang:option:${langCode}`,
266
+ selector,
267
+ source: 'agent',
268
+ success: action.success,
269
+ });
270
+ break;
271
+ }
272
+ }
273
+ }
274
+ if (action.action === 'click' || action.action === 'safe_expand' || action.action === 'select_option') {
275
+ for (const token of extractPathTokens(String(action.params.href ?? '')).slice(0, 4)) {
276
+ updates.push({
277
+ stepSignature: `nav:path:${token}`,
278
+ selector,
279
+ source: 'agent',
280
+ success: action.success,
281
+ });
282
+ }
283
+ for (const token of normalizeNavigationTokens([
284
+ action.params.elementLabel,
285
+ action.params.optionLabel,
286
+ ]).slice(0, 4)) {
287
+ updates.push({
288
+ stepSignature: `nav:label:${token}`,
289
+ selector,
290
+ source: 'agent',
291
+ success: action.success,
292
+ });
293
+ }
294
+ }
295
+ const themeLike = combined.includes('theme')
296
+ || combined.includes('appearance')
297
+ || combined.includes('dark')
298
+ || combined.includes('light')
299
+ || combined.includes('mode');
300
+ if (themeLike) {
301
+ updates.push({
302
+ stepSignature: 'theme:toggle',
303
+ selector,
304
+ source: 'agent',
305
+ success: action.success,
306
+ });
307
+ if (combined.includes('dark')) {
308
+ updates.push({
309
+ stepSignature: 'theme:option:dark',
310
+ selector,
311
+ source: 'agent',
312
+ success: action.success,
313
+ });
314
+ }
315
+ if (combined.includes('light')) {
316
+ updates.push({
317
+ stepSignature: 'theme:option:light',
318
+ selector,
319
+ source: 'agent',
320
+ success: action.success,
321
+ });
322
+ }
323
+ }
324
+ }
325
+ return updates;
326
+ }
327
+ //# sourceMappingURL=capture-selector-memory.js.map
@@ -0,0 +1,2 @@
1
+ export declare function encryptSessionField(value: unknown): unknown;
2
+ export declare function decryptSessionField<T>(value: unknown): T | undefined;
@@ -0,0 +1,22 @@
1
+ import { decrypt, encrypt, isEncryptedEnvelope } from './capture-encryption.js';
2
+ function getSecret() {
3
+ const secret = process.env.SESSION_PROFILE_ENCRYPTION_SECRET;
4
+ if (!secret) {
5
+ throw new Error('Missing SESSION_PROFILE_ENCRYPTION_SECRET for session profile encryption');
6
+ }
7
+ return secret;
8
+ }
9
+ export function encryptSessionField(value) {
10
+ if (!value)
11
+ return value;
12
+ return encrypt(JSON.stringify(value), getSecret());
13
+ }
14
+ export function decryptSessionField(value) {
15
+ if (!value)
16
+ return undefined;
17
+ if (isEncryptedEnvelope(value)) {
18
+ return JSON.parse(decrypt(value, getSecret()));
19
+ }
20
+ return value;
21
+ }
22
+ //# sourceMappingURL=capture-session-profile-encryption.js.map
@@ -0,0 +1,10 @@
1
+ export declare class CaptureStepTimeoutError extends Error {
2
+ readonly timeoutMs: number;
3
+ readonly stepLabel: string;
4
+ constructor(stepLabel: string, timeoutMs: number);
5
+ }
6
+ export declare function isCaptureStepTimeoutError(error: unknown): error is CaptureStepTimeoutError;
7
+ export declare function withCaptureStepTimeout<T>(work: () => Promise<T>, params: {
8
+ stepLabel: string;
9
+ timeoutMs: number;
10
+ }): Promise<T>;
@@ -0,0 +1,30 @@
1
+ export class CaptureStepTimeoutError extends Error {
2
+ timeoutMs;
3
+ stepLabel;
4
+ constructor(stepLabel, timeoutMs) {
5
+ super(`Timed out after ${timeoutMs}ms while ${stepLabel}.`);
6
+ this.name = "CaptureStepTimeoutError";
7
+ this.stepLabel = stepLabel;
8
+ this.timeoutMs = timeoutMs;
9
+ }
10
+ }
11
+ export function isCaptureStepTimeoutError(error) {
12
+ return error instanceof CaptureStepTimeoutError;
13
+ }
14
+ export async function withCaptureStepTimeout(work, params) {
15
+ return new Promise((resolve, reject) => {
16
+ const timer = setTimeout(() => {
17
+ reject(new CaptureStepTimeoutError(params.stepLabel, params.timeoutMs));
18
+ }, params.timeoutMs);
19
+ void work()
20
+ .then((result) => {
21
+ clearTimeout(timer);
22
+ resolve(result);
23
+ })
24
+ .catch((error) => {
25
+ clearTimeout(timer);
26
+ reject(error);
27
+ });
28
+ });
29
+ }
30
+ //# sourceMappingURL=capture-step-timeout.js.map
@@ -0,0 +1,22 @@
1
+ import type { SupabaseClient } from '@supabase/supabase-js';
2
+ type StudioBrowserBarConfig = {
3
+ url?: string;
4
+ pageTitle?: string;
5
+ tabIconUrl?: string;
6
+ } | null | undefined;
7
+ export declare function syncStudioVariantAfterCapture(params: {
8
+ supabase: SupabaseClient;
9
+ projectId: string;
10
+ presetId: string;
11
+ targetId?: string | null;
12
+ lang: string;
13
+ theme: string;
14
+ elementName: string | null;
15
+ captureId: string;
16
+ screenshotUrl: string | null;
17
+ rawScreenshotUrl?: string | null;
18
+ deviceFrame?: string | null;
19
+ browserBar?: StudioBrowserBarConfig;
20
+ matchTargetId?: boolean;
21
+ }): Promise<void>;
22
+ export {};
@@ -0,0 +1,166 @@
1
+ function isRecord(value) {
2
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
3
+ }
4
+ function getNullableString(value) {
5
+ return typeof value === 'string' && value.trim().length > 0 ? value : null;
6
+ }
7
+ function mergeStudioMockupConfig(existingConfig, deviceFrame, browserBar, rawScreenshotUrl) {
8
+ const normalizedBrowserBar = browserBar && Object.keys(browserBar).length > 0
9
+ ? {
10
+ ...(browserBar.url ? { url: browserBar.url } : {}),
11
+ ...(browserBar.pageTitle ? { pageTitle: browserBar.pageTitle } : {}),
12
+ ...(browserBar.tabIconUrl ? { tabIconUrl: browserBar.tabIconUrl } : {}),
13
+ }
14
+ : null;
15
+ const normalizedFrame = deviceFrame ?? null;
16
+ const normalizedRawUrl = rawScreenshotUrl ?? null;
17
+ const base = isRecord(existingConfig) ? { ...existingConfig } : {};
18
+ if (normalizedFrame) {
19
+ base.deviceFrame = normalizedFrame;
20
+ }
21
+ else {
22
+ delete base.deviceFrame;
23
+ }
24
+ if (normalizedRawUrl) {
25
+ base.rawScreenshotUrl = normalizedRawUrl;
26
+ }
27
+ else {
28
+ delete base.rawScreenshotUrl;
29
+ }
30
+ if (normalizedBrowserBar) {
31
+ base.browserBar = normalizedBrowserBar;
32
+ }
33
+ else {
34
+ delete base.browserBar;
35
+ }
36
+ return Object.keys(base).length > 0 ? base : null;
37
+ }
38
+ export async function syncStudioVariantAfterCapture(params) {
39
+ const { supabase, projectId, presetId, targetId = null, lang, theme, elementName, captureId, screenshotUrl, rawScreenshotUrl = null, deviceFrame = null, browserBar, matchTargetId = true, } = params;
40
+ if (!projectId || !presetId || !screenshotUrl) {
41
+ return;
42
+ }
43
+ let linkSelect = supabase
44
+ .from('screenshot_links')
45
+ .select('id, latest_capture:captures!latest_capture_id(screenshot_url, raw_screenshot_url)')
46
+ .eq('preset_id', presetId)
47
+ .eq('lang', lang)
48
+ .eq('theme', theme);
49
+ if (matchTargetId && targetId) {
50
+ linkSelect = linkSelect.eq('target_id', targetId);
51
+ }
52
+ if (elementName !== null) {
53
+ linkSelect = linkSelect.eq('element_name', elementName);
54
+ }
55
+ else {
56
+ linkSelect = linkSelect.is('element_name', null);
57
+ }
58
+ const { data: existingLinks, error: linkSelectError } = await linkSelect;
59
+ if (linkSelectError) {
60
+ console.error('[capture] failed to load screenshot links for studio sync:', linkSelectError.message);
61
+ return;
62
+ }
63
+ let linkUpdate = supabase
64
+ .from('screenshot_links')
65
+ .update({ latest_capture_id: captureId })
66
+ .eq('preset_id', presetId)
67
+ .eq('lang', lang)
68
+ .eq('theme', theme);
69
+ if (matchTargetId && targetId) {
70
+ linkUpdate = linkUpdate.eq('target_id', targetId);
71
+ }
72
+ if (elementName !== null) {
73
+ linkUpdate = linkUpdate.eq('element_name', elementName);
74
+ }
75
+ else {
76
+ linkUpdate = linkUpdate.is('element_name', null);
77
+ }
78
+ const { error: linkUpdateError } = await linkUpdate;
79
+ if (linkUpdateError) {
80
+ console.error('[capture] failed to update screenshot links:', linkUpdateError.message);
81
+ return;
82
+ }
83
+ const previousUrls = new Set();
84
+ for (const link of existingLinks ?? []) {
85
+ const latestCapture = isRecord(link.latest_capture) ? link.latest_capture : null;
86
+ const previousScreenshotUrl = getNullableString(latestCapture?.screenshot_url);
87
+ const previousRawScreenshotUrl = getNullableString(latestCapture?.raw_screenshot_url);
88
+ if (previousScreenshotUrl)
89
+ previousUrls.add(previousScreenshotUrl);
90
+ if (previousRawScreenshotUrl)
91
+ previousUrls.add(previousRawScreenshotUrl);
92
+ }
93
+ const { data: compositions, error: compositionsError } = await supabase
94
+ .from('compositions')
95
+ .select('id, pages')
96
+ .eq('project_id', projectId);
97
+ if (compositionsError) {
98
+ console.error('[capture] failed to load compositions for studio sync:', compositionsError.message);
99
+ return;
100
+ }
101
+ for (const composition of compositions ?? []) {
102
+ const pages = Array.isArray(composition.pages) ? composition.pages : [];
103
+ let changed = false;
104
+ const nextPages = pages.map((page) => {
105
+ if (!isRecord(page) || !isRecord(page.screenshot)) {
106
+ return page;
107
+ }
108
+ const screenshot = page.screenshot;
109
+ if (getNullableString(screenshot.presetId) !== presetId) {
110
+ return page;
111
+ }
112
+ const currentTargetId = getNullableString(screenshot.targetId);
113
+ const currentLang = getNullableString(screenshot.lang);
114
+ const currentTheme = getNullableString(screenshot.theme);
115
+ const currentElementName = getNullableString(screenshot.elementName);
116
+ const currentScreenshotUrl = getNullableString(screenshot.screenshotUrl);
117
+ const currentRawScreenshotUrl = getNullableString(screenshot.rawScreenshotUrl);
118
+ const targetMatches = !matchTargetId || !targetId || currentTargetId === targetId;
119
+ const exactMatch = targetMatches
120
+ && currentLang === lang
121
+ && currentTheme === theme
122
+ && currentElementName === elementName;
123
+ const legacyMatch = (!currentTargetId || !currentLang || !currentTheme)
124
+ && (currentElementName === null || currentElementName === elementName)
125
+ && ((currentScreenshotUrl !== null && previousUrls.has(currentScreenshotUrl))
126
+ || (currentRawScreenshotUrl !== null && previousUrls.has(currentRawScreenshotUrl)));
127
+ if (!exactMatch && !legacyMatch) {
128
+ return page;
129
+ }
130
+ const nextMockupConfig = mergeStudioMockupConfig(screenshot.mockupConfig, deviceFrame, browserBar, rawScreenshotUrl);
131
+ const nextScreenshot = {
132
+ ...screenshot,
133
+ presetId,
134
+ captureId,
135
+ targetId: targetId ?? currentTargetId,
136
+ lang,
137
+ theme,
138
+ elementName,
139
+ screenshotUrl,
140
+ rawScreenshotUrl,
141
+ };
142
+ if (nextMockupConfig) {
143
+ nextScreenshot.mockupConfig = nextMockupConfig;
144
+ }
145
+ if (JSON.stringify(nextScreenshot) === JSON.stringify(screenshot)) {
146
+ return page;
147
+ }
148
+ changed = true;
149
+ return {
150
+ ...page,
151
+ screenshot: nextScreenshot,
152
+ };
153
+ });
154
+ if (!changed) {
155
+ continue;
156
+ }
157
+ const { error: compositionUpdateError } = await supabase
158
+ .from('compositions')
159
+ .update({ pages: nextPages })
160
+ .eq('id', composition.id);
161
+ if (compositionUpdateError) {
162
+ console.error(`[capture] failed to sync composition ${composition.id} with latest capture:`, compositionUpdateError.message);
163
+ }
164
+ }
165
+ }
166
+ //# sourceMappingURL=capture-studio-sync.js.map
@@ -0,0 +1,54 @@
1
+ import type { CaptureCheckpoint, CapturePageIdentity, VariantCaptureRepairRecord, VariantCaptureManifest, VariantCaptureStatus, VariantValidatedCapture } from "./types.js";
2
+ import type { ScreenshotPageRunInput } from "./capture-run-optimizer.js";
3
+ export interface VariantCaptureState {
4
+ expectedPageIds: string[];
5
+ pageIdentities: Record<string, CapturePageIdentity>;
6
+ validatedCaptures: VariantValidatedCapture[];
7
+ captureStatuses: Record<string, VariantCaptureStatus>;
8
+ lastCheckpointId: string | null;
9
+ blockedReasons: Record<string, string | null>;
10
+ recoveryAttempts: Record<string, number>;
11
+ repairHistory: VariantCaptureRepairRecord[];
12
+ }
13
+ export declare function createVariantCaptureState(pageRuns: ScreenshotPageRunInput[], precomputedIdentities?: Record<string, CapturePageIdentity>): VariantCaptureState;
14
+ export declare function buildVariantManifestContext(params: {
15
+ state: VariantCaptureState;
16
+ currentPageRun: ScreenshotPageRunInput;
17
+ }): VariantCaptureManifest;
18
+ export declare function recordValidatedVariantCapture(params: {
19
+ state: VariantCaptureState;
20
+ capture: VariantValidatedCapture;
21
+ checkpointId?: string | null;
22
+ }): {
23
+ state: VariantCaptureState;
24
+ duplicateOfPageId: string | null;
25
+ };
26
+ export declare function validateVariantCaptureState(state: VariantCaptureState): {
27
+ ok: boolean;
28
+ missingPages: string[];
29
+ blockedPages: Array<{
30
+ pageId: string;
31
+ reason: string | null;
32
+ }>;
33
+ duplicatePageIds: Array<{
34
+ pageId: string;
35
+ duplicateOfPageId: string;
36
+ }>;
37
+ };
38
+ export declare function hasValidatedVariantPage(state: VariantCaptureState, pageId: string | null | undefined): boolean;
39
+ export declare function getVariantCaptureStatus(state: VariantCaptureState, pageId: string | null | undefined): VariantCaptureStatus;
40
+ export declare function markVariantCaptureInProgress(state: VariantCaptureState, pageId: string | null | undefined): VariantCaptureState;
41
+ export declare function markVariantCaptureBlocked(params: {
42
+ state: VariantCaptureState;
43
+ pageId: string | null | undefined;
44
+ reason: string;
45
+ }): VariantCaptureState;
46
+ export declare function recordVariantRecoveryAttempt(state: VariantCaptureState, pageId: string | null | undefined): VariantCaptureState;
47
+ export declare function recordVariantRepair(params: {
48
+ state: VariantCaptureState;
49
+ repair: VariantCaptureRepairRecord;
50
+ }): VariantCaptureState;
51
+ export declare function recordVariantCheckpoint(params: {
52
+ state: VariantCaptureState;
53
+ checkpoint: CaptureCheckpoint;
54
+ }): VariantCaptureState;