@datafog/node 0.5.0-beta.2 → 1.0.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.
Binary file
Binary file
Binary file
Binary file
Binary file
package/index.d.ts CHANGED
@@ -122,6 +122,11 @@ export interface ScanAndTransformConfig {
122
122
  }
123
123
 
124
124
  export type DataFogErrorCode =
125
+ | "closed_workspace" | "invalid_operation" | "operation_reused"
126
+ | "invalid_conversation" | "conversation_unknown" | "conversation_pending" | "invalid_send_intent"
127
+ | "inactive_plan" | "workspace_conflict" | "invalid_batch"
128
+ | "invalid_encoding" | "inactive_stream"
129
+ | "invalid_marker" | "invalid_snapshot" | "invalid_operation_id" | "invalid_policy" | "resource_limit"
125
130
  | "invalid_configuration"
126
131
  | "invalid_finding"
127
132
  | "review_conflict"
@@ -147,7 +152,12 @@ export declare class DataFogError extends Error {
147
152
  readonly code: DataFogErrorCode;
148
153
  readonly reason?: string;
149
154
  readonly path?: string;
150
- readonly findingIndex?: number;
155
+ readonly findingIndex?: number | null;
156
+ readonly inputIndex?: number | null;
157
+ readonly markerIssues?: readonly WorkspaceMarkerIssue[];
158
+ readonly reviewConflicts?: readonly WorkspaceReviewConflict[];
159
+ readonly delivered?: WorkspaceRestoreProgress;
160
+ readonly complete?: false;
151
161
  }
152
162
 
153
163
  export interface KeyProviderRequest {
@@ -245,6 +255,251 @@ export declare function scanAndTransformStructured(data: JsonDocument, config: S
245
255
 
246
256
  export interface StructuredTransformResult { readonly data: JsonDocument; readonly transformations: StructuredTransformation[]; }
247
257
  export interface StructuredRestoreResult { readonly data: JsonDocument; readonly restorations: StructuredRestoration[]; }
258
+
259
+ /** Caller-declared review origin, not an authorization or authenticity claim. */
260
+ export type WorkspaceFindingSource = "scanner" | "recall" | "user";
261
+
262
+ /** Provisional workspace input; exact values and type codes are never normalized. */
263
+ export interface WorkspaceFinding {
264
+ readonly entityType: string;
265
+ readonly matchedText: string;
266
+ readonly range: TextRange & { readonly unit: "utf8_bytes" };
267
+ readonly confidence?: number | null;
268
+ readonly detectorName?: string | null;
269
+ readonly detectorVersion?: string | null;
270
+ /** Defaults to scanner for existing callers; not inferred from detectorName. */
271
+ readonly source?: WorkspaceFindingSource;
272
+ /** Defaults to true. False does not waive host approval/save/activation. */
273
+ readonly needsReview?: boolean;
274
+ }
275
+ /** One batch input; finding offsets are relative to this text only. */
276
+ export interface WorkspaceInput {
277
+ readonly text: string;
278
+ readonly findings: readonly WorkspaceFinding[];
279
+ }
280
+ export interface WorkspaceHead { readonly revision: string; readonly commitId: readonly number[]; }
281
+ export interface WorkspaceRecalledFinding {
282
+ readonly finding: WorkspaceFinding & {readonly source: "recall"; readonly needsReview: true};
283
+ readonly byteRange: TextRange;
284
+ readonly codepointRange: TextRange;
285
+ readonly utf16Range: TextRange;
286
+ }
287
+ export interface WorkspaceRecallResult {
288
+ readonly head: WorkspaceHead;
289
+ /** All matches or an explicit failure. Empty is not a clean-text verdict. */
290
+ readonly complete: true;
291
+ readonly findings: readonly WorkspaceRecalledFinding[];
292
+ }
293
+ export interface WorkspacePreview {
294
+ readonly text: string;
295
+ readonly transformations: readonly {
296
+ readonly source: WorkspaceFindingSource;
297
+ readonly needsReview: boolean;
298
+ readonly sourceByteRange: TextRange;
299
+ readonly sourceCodepointRange: TextRange;
300
+ readonly sourceUtf16Range: TextRange;
301
+ readonly outputByteRange: TextRange;
302
+ readonly outputCodepointRange: TextRange;
303
+ readonly outputUtf16Range: TextRange;
304
+ }[];
305
+ }
306
+ export type WorkspaceTypeRule = "hide" | "keep";
307
+ export type WorkspaceChoiceDisposition = "keep" | "not_sensitive";
308
+ export interface WorkspacePolicyEdits {
309
+ readonly rules: readonly {readonly entityType: string; readonly rule: WorkspaceTypeRule | null}[];
310
+ readonly choices: readonly {readonly entityType: string; readonly value: string; readonly disposition: WorkspaceChoiceDisposition | null}[];
311
+ }
312
+ export interface WorkspacePolicy {
313
+ readonly snapshotVersion: 1 | 2 | 3;
314
+ readonly defaultRule: "hide";
315
+ readonly protectedTypes: readonly string[];
316
+ readonly rules: readonly {readonly entityType: string; readonly rule: WorkspaceTypeRule}[];
317
+ readonly choices: readonly {readonly entityType: string; readonly value: string; readonly disposition: WorkspaceChoiceDisposition}[];
318
+ }
319
+ export interface WorkspacePolicyPreview {
320
+ readonly rules: readonly {readonly entityType: string; readonly before: WorkspaceTypeRule | null; readonly after: WorkspaceTypeRule | null}[];
321
+ readonly choices: readonly {readonly entityType: string; readonly value: string; readonly before: WorkspaceChoiceDisposition | null; readonly after: WorkspaceChoiceDisposition | null}[];
322
+ }
323
+ export interface PreparedWorkspaceChange {
324
+ /** Intent IDs are owned metadata, never proof of a send. */
325
+ sendIntents(): readonly WorkspaceSendIntent[];
326
+ /** Warn before a saved format upgrade: beta.1 cannot open v2, and beta.1/beta.2 cannot open v3. */
327
+ snapshotFormat(): {readonly currentVersion: 1 | 2 | 3; readonly candidateVersion: 1 | 2 | 3};
328
+ /** Present only for settings edits; values are sensitive and remain local. */
329
+ policyPreview(): WorkspacePolicyPreview | null;
330
+ /** Requires a single-input plan. */
331
+ preview(): WorkspacePreview;
332
+ previews(): readonly WorkspacePreview[];
333
+ heads(): { readonly expectedHead: WorkspaceHead; readonly candidateHead: WorkspaceHead };
334
+ /** Sensitive plaintext for the trusted encrypting host only. */
335
+ exportCandidateBundle(): Uint8Array;
336
+ discard(): void;
337
+ }
338
+ /** Content-free source or output issue ranges; never contains a source or restored value. */
339
+ export interface WorkspaceMarkerIssue {
340
+ /** unknown: well formed but never committed here; damaged: unparseable marker; unbracketed: committed code without brackets. */
341
+ readonly kind: "unknown" | "damaged" | "unbracketed" | "not_issued_in_conversation";
342
+ readonly byteRange: TextRange;
343
+ readonly codepointRange: TextRange;
344
+ readonly utf16Range: TextRange;
345
+ }
346
+ export interface WorkspaceRestoreReport {
347
+ /** Present only when every marker resolved and `issues` is empty. */
348
+ readonly text: string | null;
349
+ readonly restoredMarkers: number;
350
+ readonly issues: readonly WorkspaceMarkerIssue[];
351
+ }
352
+ export interface WorkspaceReviewInput {
353
+ readonly text: string;
354
+ /** Model/rule findings; caller source flags are ignored for channel attribution. */
355
+ readonly scannerFindings: readonly WorkspaceFinding[];
356
+ readonly userSelections: readonly WorkspaceFinding[];
357
+ readonly corrections?: readonly { readonly candidateKey: string; readonly replacement: WorkspaceFinding }[];
358
+ }
359
+ export interface WorkspaceReviewConflict {
360
+ readonly kind: "protected_coverage" | "type_conflict";
361
+ readonly findingIndex: number;
362
+ readonly byteRange: TextRange;
363
+ readonly codepointRange: TextRange;
364
+ readonly utf16Range: TextRange;
365
+ }
366
+ export interface WorkspaceReviewCandidate {
367
+ /** Binds exact source/head/type/range; changes on source edits or saved-head changes. */
368
+ readonly key: string;
369
+ readonly finding: WorkspaceFinding;
370
+ readonly sources: readonly ("scanner" | "recall" | "user")[];
371
+ readonly byteRange: TextRange;
372
+ readonly codepointRange: TextRange;
373
+ readonly utf16Range: TextRange;
374
+ /** Committed policy requires this candidate to be hidden. */
375
+ readonly eligible: boolean;
376
+ /** Conflict-free selection only; false for every candidate while conflicts remain. Never handoff permission. */
377
+ readonly selected: boolean;
378
+ }
379
+ export interface WorkspaceReviewResult {
380
+ readonly head: WorkspaceHead;
381
+ readonly candidates: readonly WorkspaceReviewCandidate[];
382
+ readonly conflicts: readonly WorkspaceReviewConflict[];
383
+ }
384
+ export interface WorkspaceOperation { readonly id: Uint8Array; readonly occurredAtMs: number; }
385
+ export interface WorkspaceConversation {
386
+ /** Private immutable host-generated ID, independent of the site's ID. */
387
+ readonly id: Uint8Array | readonly number[];
388
+ readonly surface: string;
389
+ /** Required null for an initially unbound first chat. Bind once after observed send. */
390
+ readonly siteId: string | null;
391
+ /** Immutable pre-send maximum UTF-8 bytes; the host must substantiate this bound. */
392
+ readonly siteIdByteBudget: number;
393
+ }
394
+ export interface WorkspaceConversationInput { readonly input: WorkspaceReviewInput; readonly conversation: WorkspaceConversation; }
395
+ export interface WorkspaceSendIntent { readonly preparationId: Uint8Array | readonly number[]; readonly inputIndex: number; }
396
+ export interface WorkspaceConversationState { readonly conversation: WorkspaceConversation; readonly pendingIntents: number; readonly issuedMarkers: number; }
397
+ export type WorkspaceSendStatus = "pending" | "observed_sent" | "definitely_not_sent";
398
+ export type WorkspaceOutcomeKind = "copy_confirmed" | "user_cancelled" | "blocked_input" | "restore_blocked";
399
+ export interface WorkspaceOutcome { readonly kind: WorkspaceOutcomeKind; readonly issueCount: number; }
400
+ export interface WorkspaceOperationRecord {
401
+ readonly id: readonly number[];
402
+ readonly kind: "create" | "prepare" | "recovery" | "policy_change" | "conversation_prepare" | "send_confirmed" | "send_not_sent" | WorkspaceOutcomeKind;
403
+ readonly occurredAtMs: number | null;
404
+ readonly preparedInputs: number;
405
+ readonly transformedSpans: number;
406
+ }
407
+ export interface WorkspaceOperationPage {
408
+ readonly records: readonly WorkspaceOperationRecord[];
409
+ readonly total: number;
410
+ readonly offset: number;
411
+ readonly nextOffset: number | null;
412
+ }
413
+ export interface WorkspaceHistoryCoverage {
414
+ readonly recordedHistoryOnly: true;
415
+ readonly preUpgradeHistory: "unknown" | "not_applicable";
416
+ }
417
+ export interface WorkspaceCounts {
418
+ readonly operations: number; readonly preparedInputs: number; readonly transformedSpans: number;
419
+ readonly confirmedHandoffs: number; readonly reportedBlockedAttempts: number;
420
+ }
421
+ export type WorkspaceRestoreEncoding = "utf8" | "utf16";
422
+ export interface WorkspaceRestoreProgress {
423
+ readonly bytes: number;
424
+ readonly codepoints: number;
425
+ readonly utf16: number;
426
+ readonly restoredMarkers: number;
427
+ }
428
+ export interface WorkspaceRestoreChunk {
429
+ readonly text: string;
430
+ readonly delivered: WorkspaceRestoreProgress;
431
+ /** Only successful finalization is complete; prior chunks cannot be retracted. */
432
+ readonly complete: boolean;
433
+ }
434
+ export interface WorkspaceBufferedRestore {
435
+ feed(bytes: Uint8Array): void;
436
+ feedUtf16(units: Uint16Array): void;
437
+ finalize(): WorkspaceRestoreReport;
438
+ reset(): void;
439
+ close(): void;
440
+ }
441
+ export interface WorkspaceProgressiveRestore {
442
+ feed(bytes: Uint8Array): WorkspaceRestoreChunk;
443
+ feedUtf16(units: Uint16Array): WorkspaceRestoreChunk;
444
+ finalize(): WorkspaceRestoreChunk;
445
+ reset(): void;
446
+ close(): void;
447
+ }
448
+ export class Workspace {
449
+ private constructor();
450
+ static createWithOperation(namespace: Uint8Array, operation: WorkspaceOperation): Workspace;
451
+ static create(namespace: Uint8Array, operationId: Uint8Array): Workspace;
452
+ static openBundle(bytes: Uint8Array): Workspace;
453
+ head(): WorkspaceHead;
454
+ namespace(): Uint8Array;
455
+ exportBundle(): Uint8Array;
456
+ /** Read committed values only, returning all overlaps in original coordinates. No aliases, plans or state changes. */
457
+ recall(text: string): WorkspaceRecallResult;
458
+ /** Combine supplied scanner/manual findings with committed recall, without plans or state mutation. */
459
+ review(input: WorkspaceReviewInput): WorkspaceReviewResult;
460
+ /** Recompute review for every input. Any unresolved overlap rejects the whole batch before a plan exists. Approval and durable save remain required. */
461
+ prepareReview(inputs: readonly WorkspaceReviewInput[], operationId: Uint8Array): PreparedWorkspaceChange;
462
+ operations(): readonly WorkspaceOperationRecord[];
463
+ operationsPage(offset: number, limit: number): WorkspaceOperationPage;
464
+ counts(): WorkspaceCounts;
465
+ historyCoverage(): WorkspaceHistoryCoverage;
466
+ prepareWithOperation(text: string, findings: readonly WorkspaceFinding[], operation: WorkspaceOperation): PreparedWorkspaceChange;
467
+ prepareBatchWithOperation(inputs: readonly WorkspaceInput[], operation: WorkspaceOperation): PreparedWorkspaceChange;
468
+ prepareRecoveryWithOperation(operation: WorkspaceOperation): PreparedWorkspaceChange;
469
+ prepareOutcome(outcome: WorkspaceOutcome, operation: WorkspaceOperation): PreparedWorkspaceChange;
470
+ /** Funds the later resolution before any external handoff; save/activate explicitly. */
471
+ prepareConversationSend(inputs: readonly WorkspaceConversationInput[], operation: WorkspaceOperation): PreparedWorkspaceChange;
472
+ conversation(logicalId: Uint8Array): WorkspaceConversationState;
473
+ conversationForSite(surface: string, siteId: string): Uint8Array;
474
+ sendIntentStatus(intent: WorkspaceSendIntent): WorkspaceSendStatus;
475
+ /** Trusted actual-send observation only; binding and issuance save atomically. */
476
+ prepareObservedSend(intent: WorkspaceSendIntent, logicalId: Uint8Array, surface: string, siteId: string, operation: WorkspaceOperation): PreparedWorkspaceChange;
477
+ /** Trusted definite non-delivery, never a generic timeout or manual send assertion. */
478
+ prepareDefinitelyNotSent(intent: WorkspaceSendIntent, logicalId: Uint8Array, operation: WorkspaceOperation): PreparedWorkspaceChange;
479
+ restoreInConversation(text: string, logicalId: Uint8Array): string;
480
+ restoreReportInConversation(text: string, logicalId: Uint8Array): WorkspaceRestoreReport;
481
+ bufferedRestoreInConversation(logicalId: Uint8Array, encoding?: WorkspaceRestoreEncoding): WorkspaceBufferedRestore;
482
+ progressiveRestoreInConversation(logicalId: Uint8Array, encoding?: WorkspaceRestoreEncoding): WorkspaceProgressiveRestore;
483
+ policy(): WorkspacePolicy;
484
+ /** Explicit exact-value corrections can override real PII; source flags cannot. Null removes an override. */
485
+ preparePolicyChanges(edits: WorkspacePolicyEdits, operationId: Uint8Array): PreparedWorkspaceChange;
486
+ /** Stage a fresh recovery head without changing saved markers. No text previews or handoff permission. */
487
+ prepareRecovery(operationId: Uint8Array): PreparedWorkspaceChange;
488
+ /** Supplied findings only: omissions are never proof of PII-free input; use prepareReview for combined recall. Committed full markers pass through; damaged/unknown markers and bare committed codes reject with markerIssues before a plan is created. Findings must not overlap markers. */
489
+ prepare(text: string, findings: readonly WorkspaceFinding[], operationId: Uint8Array): PreparedWorkspaceChange;
490
+ /** Atomic batch: preserves order, validates committed markers, and applies aggregate limits. */
491
+ prepareBatch(inputs: readonly WorkspaceInput[], operationId: Uint8Array): PreparedWorkspaceChange;
492
+ /** The trusted host must confirm atomic persistence first. Success consumes this plan and drops retained plans based on older heads; failure preserves current plans. */
493
+ activateSaved(change: PreparedWorkspaceChange, expectedHead: WorkspaceHead, savedHead: WorkspaceHead): void;
494
+ /** All-or-nothing; throws `invalid_marker` when `restoreReport` would list an issue. */
495
+ bufferedRestore(encoding?: WorkspaceRestoreEncoding): WorkspaceBufferedRestore;
496
+ progressiveRestore(encoding?: WorkspaceRestoreEncoding): WorkspaceProgressiveRestore;
497
+ restore(text: string): string;
498
+ /** All-or-nothing restoration that locates every unknown, damaged or unbracketed marker. */
499
+ restoreReport(text: string): WorkspaceRestoreReport;
500
+ close(): void;
501
+ }
502
+ export function workspaceByteRangeFromUtf16(text: string, start: number, end: number): TextRange & { readonly unit: "utf8_bytes" };
248
503
  export declare class ModelCancellation {
249
504
  constructor()
250
505
  cancel(): void
@@ -258,6 +513,10 @@ export declare class ModelSessionNative {
258
513
  close(): Promise<void>
259
514
  }
260
515
 
516
+ /** Factory-only private stream capability; dropping the owner clears retained children. */
517
+
518
+ /** Raw binding; the package wrapper owns opaque plan objects and safe error conversion. */
519
+
261
520
  export declare function discoverFields(dataJson: JsonDocument, config?: StructuredScanConfig | undefined): Array<FieldMapping>
262
521
 
263
522
  export interface FieldMapping {
@@ -324,15 +583,10 @@ export interface PreparedStructuredScan {
324
583
  selectors: Array<KeySelector>
325
584
  }
326
585
 
327
- export declare function prepareScanAndTransform(text: string, config: ScanAndTransformConfig): PreparedScanAndTransform
328
586
 
329
- export declare function prepareStructuredScanAndTransform(text: string, config: unknown): PreparedStructuredScan
330
587
 
331
- export declare function requiredKeySelectors(text: string, findings: FindingInput[], config: TransformationConfig): Array<KeySelector>
332
588
 
333
- export declare function requiredRestoreItems(text: string, context: PrivacyContext): Array<RestoreItem>
334
589
 
335
- export declare function requiredTokenizationItems(text: string, findings: FindingInput[], config: TransformationConfig, context?: PrivacyContext | undefined): Array<TokenizeItem>
336
590
 
337
591
  export interface ResolvedKeyInput {
338
592
  selectorIndex: number
@@ -368,7 +622,6 @@ export interface RestoreResult {
368
622
  readonly restorations: Array<Restoration>
369
623
  }
370
624
 
371
- export declare function restoreWithResults(text: string, context: PrivacyContext, results: Array<RestoredValueInput>): RestoreResult
372
625
 
373
626
  /** Scan text for supported PII findings. */
374
627
  export declare function scan(text: string, config?: ScanConfig | undefined): Array<Finding>
@@ -389,34 +642,27 @@ export interface StructuredFindingInput {
389
642
  }
390
643
 
391
644
  /** Scan text and transform the detected findings. */
392
- export declare function structuredRequiredKeySelectors(text: JsonDocument, findings: StructuredFindingInput[], config: TransformationConfig): Array<KeySelector>
393
645
 
394
- export declare function structuredRequiredRestoreItems(text: JsonDocument, context: PrivacyContext): Array<RestoreItem>
395
646
 
396
- export declare function structuredRequiredTokenizationItems(text: JsonDocument, findings: StructuredFindingInput[], config: TransformationConfig, context?: PrivacyContext | undefined): Array<TokenizeItem>
397
647
 
398
648
  export interface StructuredRestoration {
399
649
  path: string
400
650
  restoration: Restoration
401
651
  }
402
652
 
403
- export declare function structuredRestoreWithResults(text: JsonDocument, context: PrivacyContext, results: Array<RestoredValueInput>): NativeStructuredRestoreResult
404
653
 
405
- export declare function structuredScanAndTransform(text: JsonDocument, config: StructuredScanAndTransformConfig): NativeStructuredTransformResult
406
654
 
407
655
  export interface StructuredScanResult {
408
656
  mappings: Array<FieldMapping>
409
657
  findings: Array<StructuredFinding>
410
658
  }
411
659
 
412
- export declare function structuredTransform(text: JsonDocument, findings: StructuredFindingInput[], config: TransformationConfig): NativeStructuredTransformResult
413
660
 
414
661
  export interface StructuredTransformation {
415
662
  path: string
416
663
  transformation: Transformation
417
664
  }
418
665
 
419
- export declare function structuredTransformWithProviderResults(text: JsonDocument, findings: StructuredFindingInput[], config: TransformationConfig, context: PrivacyContext | undefined, resolvedKeys: Array<ResolvedKeyInput>, tokenResults: Array<TokenizeResultInput>): NativeStructuredTransformResult
420
666
 
421
667
  export interface TextRange {
422
668
  readonly start: number
@@ -462,6 +708,7 @@ export interface TransformResult {
462
708
  readonly transformations: Array<Transformation>
463
709
  }
464
710
 
465
- export declare function transformWithProviderResults(text: string, findings: FindingInput[], config: TransformationConfig, context: PrivacyContext | undefined, resolvedKeys: Array<ResolvedKeyInput>, tokenResults: Array<TokenizeResultInput>): TransformResult
466
711
 
467
- export declare function transformWithResolvedKeys(text: string, findings: FindingInput[], config: TransformationConfig, resolvedKeys: Array<ResolvedKeyInput>): TransformResult
712
+
713
+ /** Workspace API contract implemented by this compiled Core. */
714
+ export declare function workspaceContractVersion(): number
package/index.js CHANGED
@@ -1,5 +1,7 @@
1
1
  import { Buffer } from "node:buffer";
2
2
  import {
3
+ NativeWorkspace,
4
+ workspaceContractVersion as nativeWorkspaceContractVersion,
3
5
  openModelSession as nativeOpenModelSession,
4
6
  structuredTransform as nativeStructuredTransform,
5
7
  structuredScanAndTransform as nativeStructuredScanAndTransform,
@@ -24,6 +26,9 @@ import {
24
26
 
25
27
  export { ModelCancellation, ModelSessionNative, openModelSession } from "./native.js";
26
28
 
29
+ /** Workspace behavior contract of the loaded native Core, independent of package version. */
30
+ export function workspaceContractVersion() { return nativeWorkspaceContractVersion(); }
31
+
27
32
  // An unknown option, such as a misspelled `config`, would otherwise be ignored.
28
33
  function modelScanOptions(options, allowed) {
29
34
  if (options === null || typeof options !== "object" || Array.isArray(options)) {
@@ -66,13 +71,18 @@ export class ModelSession {
66
71
  }
67
72
 
68
73
  export class DataFogError extends Error {
69
- constructor({ code, reason, message, path, findingIndex }) {
74
+ constructor({ code, reason, message, path, findingIndex, inputIndex, markerIssues, reviewConflicts, delivered, complete }) {
70
75
  super(message);
71
76
  this.name = "DataFogError";
72
77
  this.code = code;
73
78
  this.reason = reason;
74
79
  this.path = path;
75
80
  this.findingIndex = findingIndex;
81
+ this.inputIndex = inputIndex;
82
+ this.markerIssues = markerIssues;
83
+ this.reviewConflicts = reviewConflicts;
84
+ this.delivered = delivered;
85
+ this.complete = complete;
76
86
  }
77
87
  }
78
88
 
@@ -429,3 +439,443 @@ function structuredOptions(value) {
429
439
  throw new DataFogError({code:"invalid_configuration", reason:"invalid_type", path:"", message:"structured request options must be JSON-compatible"});
430
440
  }
431
441
  }
442
+
443
+ const workspacePlans = new WeakMap();
444
+ const workspaceFinalizer = new FinalizationRegistry(({state, handle}) => {
445
+ if (!state.closed) state.native.release(handle);
446
+ });
447
+ const workspaceToken = Symbol("workspace");
448
+ function workspacePlan(change) {
449
+ const plan = workspacePlans.get(change);
450
+ if (!plan) throw workspaceFailure("inactive_plan");
451
+ return plan;
452
+ }
453
+ function workspaceFailure(code) {
454
+ return new DataFogError({ code, message: code });
455
+ }
456
+ function workspaceText(text, limit) {
457
+ if (typeof text !== "string" || !text.isWellFormed()) throw workspaceFailure("invalid_finding");
458
+ if (text.length > limit || new TextEncoder().encode(text).length > limit) throw workspaceFailure("resource_limit");
459
+ }
460
+ function workspaceBytes(bytes, limit, exact = false) {
461
+ if (!(bytes instanceof Uint8Array)) throw workspaceFailure(exact ? "invalid_operation_id" : "invalid_snapshot");
462
+ if (exact ? bytes.length !== limit : bytes.length > limit) throw workspaceFailure(exact ? "invalid_operation_id" : "resource_limit");
463
+ }
464
+ function workspaceCall(state, method, ...args) {
465
+ if (state.closed) throw workspaceFailure("closed_workspace");
466
+ try {
467
+ const value = state.native[method](...args.map(value => value instanceof Uint8Array ? Buffer.from(value) : value));
468
+ if (value instanceof Uint8Array) {
469
+ try { return new Uint8Array(value); } finally { value.fill(0); }
470
+ }
471
+ return value;
472
+ }
473
+ catch (error) { throw normalizeError(error, "internal_error"); }
474
+ }
475
+
476
+ // Bound the fixed finding transport before JSON.stringify can expand repeated
477
+ // references or large nested values. Core still owns span/type/selection semantics.
478
+ function workspaceFindingsJson(findings) {
479
+ const maximum = 16 * 1024 * 1024;
480
+ let budget = 2;
481
+ function object(value, allowed) {
482
+ if (!value || typeof value !== "object" || Array.isArray(value) || ![Object.prototype, null].includes(Object.getPrototypeOf(value))) throw workspaceFailure("invalid_finding");
483
+ const properties = Object.getOwnPropertyDescriptors(value);
484
+ for (const key of Reflect.ownKeys(properties)) {
485
+ const property = properties[key];
486
+ if (typeof key !== "string" || !allowed.includes(key) || !property.enumerable || !("value" in property)) throw workspaceFailure("invalid_finding");
487
+ }
488
+ return Object.fromEntries(Object.entries(properties).map(([key, property]) => [key, property.value]));
489
+ }
490
+ function string(value) {
491
+ if (typeof value !== "string" || !value.isWellFormed()) throw workspaceFailure("invalid_finding");
492
+ if (value.length > maximum) throw workspaceFailure("resource_limit");
493
+ budget += 2;
494
+ for (const character of value) {
495
+ const point = character.codePointAt(0);
496
+ budget += point < 32 ? 6 : point === 34 || point === 92 ? 2 : point < 128 ? 1 : point < 2048 ? 2 : point < 65536 ? 3 : 4;
497
+ if (budget > maximum) throw workspaceFailure("resource_limit");
498
+ }
499
+ }
500
+ for (let i = 0; i < findings.length; i++) {
501
+ const item = Object.getOwnPropertyDescriptor(findings, String(i));
502
+ if (!item?.enumerable || !("value" in item)) throw workspaceFailure("invalid_finding");
503
+ const value = item.value;
504
+ const f = object(value, ["entityType", "matchedText", "range", "confidence", "detectorName", "detectorVersion", "source", "needsReview"]);
505
+ const range = object(f.range, ["unit", "start", "end"]);
506
+ budget += 256; // A conservative bound for keys, punctuation and scalar numbers.
507
+ string(f.entityType); string(f.matchedText); string(range.unit);
508
+ for (const field of ["detectorName", "detectorVersion"]) if (f[field] !== undefined && f[field] !== null) string(f[field]);
509
+ if (f.source !== undefined) string(f.source);
510
+ if (f.needsReview !== undefined && typeof f.needsReview !== "boolean") throw workspaceFailure("invalid_finding");
511
+ if (!Number.isSafeInteger(range.start) || !Number.isSafeInteger(range.end) || (f.confidence !== undefined && f.confidence !== null && (typeof f.confidence !== "number" || !Number.isFinite(f.confidence)))) throw workspaceFailure("invalid_finding");
512
+ if (budget > maximum) throw workspaceFailure("resource_limit");
513
+ }
514
+ try { return structuredJson(findings, true); }
515
+ catch { throw workspaceFailure("invalid_finding"); }
516
+ }
517
+ function workspacePolicyJson(edits) {
518
+ const maximum = 16 * 1024 * 1024;
519
+ let budget = 2;
520
+ function object(value, fields) {
521
+ if (!value || typeof value !== "object" || Array.isArray(value) || ![Object.prototype, null].includes(Object.getPrototypeOf(value))) throw workspaceFailure("invalid_policy");
522
+ const properties = Object.getOwnPropertyDescriptors(value);
523
+ if (Reflect.ownKeys(properties).length !== fields.length) throw workspaceFailure("invalid_policy");
524
+ for (const field of fields) if (!properties[field]?.enumerable || !("value" in properties[field])) throw workspaceFailure("invalid_policy");
525
+ return Object.fromEntries(fields.map(field => [field,properties[field].value]));
526
+ }
527
+ function string(value) {
528
+ if (typeof value !== "string" || !value.isWellFormed()) throw workspaceFailure("invalid_policy");
529
+ if (value.length > 1024 * 1024) throw workspaceFailure("resource_limit");
530
+ budget += new TextEncoder().encode(value).length * 6 + 128;
531
+ if (budget > maximum) throw workspaceFailure("resource_limit");
532
+ }
533
+ const request = object(edits,["rules","choices"]);
534
+ if (!Array.isArray(request.rules) || !Array.isArray(request.choices)) throw workspaceFailure("invalid_policy");
535
+ if (request.rules.length + request.choices.length > 10000) throw workspaceFailure("resource_limit");
536
+ for (const [name,fields] of [["rules",["entityType","rule"]],["choices",["entityType","value","disposition"]]]) {
537
+ const values=request[name];
538
+ if (Reflect.ownKeys(values).length !== values.length+1) throw workspaceFailure("invalid_policy");
539
+ for (let i=0;i<values.length;i++) {
540
+ const property=Object.getOwnPropertyDescriptor(values,String(i));
541
+ if (!property?.enumerable || !("value" in property)) throw workspaceFailure("invalid_policy");
542
+ const row=object(property.value,fields);
543
+ string(row.entityType);
544
+ if (name === "choices") string(row.value);
545
+ const action = name === "rules" ? row.rule : row.disposition;
546
+ if (action !== null) string(action);
547
+ }
548
+ }
549
+ try { return structuredJson(edits,true); } catch { throw workspaceFailure("invalid_policy"); }
550
+ }
551
+ function workspaceReviewJson(input) {
552
+ function object(value, fields, required) {
553
+ if (!value || typeof value !== "object" || Array.isArray(value) || ![Object.prototype,null].includes(Object.getPrototypeOf(value))) throw workspaceFailure("invalid_finding");
554
+ const props=Object.getOwnPropertyDescriptors(value);
555
+ for (const name of Reflect.ownKeys(props)) if (typeof name !== "string" || !fields.includes(name) || !props[name].enumerable || !("value" in props[name])) throw workspaceFailure("invalid_finding");
556
+ for (const name of required) if (!props[name]) throw workspaceFailure("invalid_finding");
557
+ return Object.fromEntries(Object.entries(props).map(([name,property])=>[name,property.value]));
558
+ }
559
+ function array(value) {
560
+ if (!Array.isArray(value)) throw workspaceFailure("invalid_finding");
561
+ if (value.length>10000) throw workspaceFailure("resource_limit");
562
+ if (Reflect.ownKeys(value).length !== value.length+1) throw workspaceFailure("invalid_finding");
563
+ return value;
564
+ }
565
+ const fields=object(input,["text","scannerFindings","userSelections","corrections"],["text","scannerFindings","userSelections"]);
566
+ workspaceText(fields.text,1024*1024);
567
+ const scanner=array(fields.scannerFindings), user=array(fields.userSelections), corrections=array(fields.corrections === undefined ? [] : fields.corrections);
568
+ if (scanner.length+user.length+corrections.length>10000) throw workspaceFailure("resource_limit");
569
+ const scannerJson=workspaceFindingsJson(scanner), userJson=workspaceFindingsJson(user);
570
+ let budget=new TextEncoder().encode(scannerJson).length+new TextEncoder().encode(userJson).length+new TextEncoder().encode(JSON.stringify(fields.text)).length+128;
571
+ const corrected=[];
572
+ for(let index=0;index<corrections.length;index++) {
573
+ const item=Object.getOwnPropertyDescriptor(corrections,String(index));
574
+ if(!item?.enumerable || !("value" in item)) throw workspaceFailure("invalid_finding");
575
+ const correction=object(item.value,["candidateKey","replacement"],["candidateKey","replacement"]);
576
+ if(typeof correction.candidateKey !== "string" || !/^[a-f0-9]{64}$/.test(correction.candidateKey)) throw workspaceFailure("workspace_conflict");
577
+ const replacement=workspaceFindingsJson([correction.replacement]).slice(1,-1);
578
+ const part=`{"candidateKey":${JSON.stringify(correction.candidateKey)},"replacement":${replacement}}`;
579
+ budget+=new TextEncoder().encode(part).length+2;
580
+ if(budget>16*1024*1024) throw workspaceFailure("resource_limit");
581
+ corrected.push(part);
582
+ }
583
+ if(budget>16*1024*1024) throw workspaceFailure("resource_limit");
584
+ return `{"text":${JSON.stringify(fields.text)},"scannerFindings":${scannerJson},"userSelections":${userJson},"corrections":[${corrected.join(",")}]}`;
585
+ }
586
+
587
+ function workspaceOperation(operation) {
588
+ if (!operation || typeof operation !== "object" || ![Object.prototype, null].includes(Object.getPrototypeOf(operation))) throw workspaceFailure("invalid_operation");
589
+ const properties = Object.getOwnPropertyDescriptors(operation);
590
+ if (Reflect.ownKeys(properties).length !== 2 || !properties.id?.enumerable || !("value" in properties.id) || !properties.occurredAtMs?.enumerable || !("value" in properties.occurredAtMs)) throw workspaceFailure("invalid_operation");
591
+ const id = properties.id.value, time = properties.occurredAtMs.value;
592
+ workspaceBytes(id, 16, true);
593
+ if (!Number.isSafeInteger(time) || time < 0 || time > 253402300799999) throw workspaceFailure("invalid_operation");
594
+ return {id, json: JSON.stringify({id: Array.from(id), occurredAtMs: time})};
595
+ }
596
+ function conversationRecord(value,fields) {
597
+ if(!value || typeof value!=="object" || ![Object.prototype,null].includes(Object.getPrototypeOf(value))) throw workspaceFailure("invalid_conversation");
598
+ const properties=Object.getOwnPropertyDescriptors(value);
599
+ if(Reflect.ownKeys(properties).length!==fields.length) throw workspaceFailure("invalid_conversation");
600
+ const result={};
601
+ for(const field of fields) {
602
+ if(!properties[field]?.enumerable || !("value" in properties[field])) throw workspaceFailure("invalid_conversation");
603
+ result[field]=properties[field].value;
604
+ }
605
+ return result;
606
+ }
607
+ function conversationId(value) {
608
+ if(value instanceof Uint8Array) {if(value.length!==16)throw workspaceFailure("invalid_conversation");return Array.from(value);}
609
+ if(!Array.isArray(value) || value.length!==16 || Reflect.ownKeys(value).length!==17) throw workspaceFailure("invalid_conversation");
610
+ const bytes=[];
611
+ for(let i=0;i<16;i++) {
612
+ const item=Object.getOwnPropertyDescriptor(value,String(i));
613
+ if(!item?.enumerable || !("value" in item) || !Number.isInteger(item.value) || item.value<0 || item.value>255) throw workspaceFailure("invalid_conversation");
614
+ bytes.push(item.value);
615
+ }
616
+ return bytes;
617
+ }
618
+ function conversationText(value) {
619
+ if(typeof value!=="string" || !value.isWellFormed())throw workspaceFailure("invalid_conversation");
620
+ workspaceText(value,16*1024*1024);
621
+ }
622
+ function conversationInput(value) {
623
+ const fields=conversationRecord(value,["id","surface","siteId","siteIdByteBudget"]);
624
+ fields.id=conversationId(fields.id);
625
+ conversationText(fields.surface);
626
+ if(fields.siteId!==null) conversationText(fields.siteId);
627
+ if(!Number.isSafeInteger(fields.siteIdByteBudget) || fields.siteIdByteBudget<1 || fields.siteIdByteBudget>16*1024*1024) throw workspaceFailure("invalid_conversation");
628
+ return fields;
629
+ }
630
+ function sendIntentJson(value) {
631
+ let fields;
632
+ try {
633
+ fields=conversationRecord(value,["preparationId","inputIndex"]);
634
+ fields.preparationId=conversationId(fields.preparationId);
635
+ if(!Number.isSafeInteger(fields.inputIndex) || fields.inputIndex<0 || fields.inputIndex>=32) throw workspaceFailure("invalid_send_intent");
636
+ } catch {throw workspaceFailure("invalid_send_intent");}
637
+ return JSON.stringify(fields);
638
+ }
639
+ class PreparedWorkspaceChange {
640
+ constructor(state, handle) { workspacePlans.set(this, { state, handle }); workspaceFinalizer.register(this, {state, handle}, this); Object.freeze(this); }
641
+ sendIntents() { const {state, handle} = workspacePlan(this); return JSON.parse(workspaceCall(state,"send_intents",handle)); }
642
+ snapshotFormat() { const {state, handle} = workspacePlan(this); return JSON.parse(workspaceCall(state,"snapshot_format",handle)); }
643
+ policyPreview() { const {state, handle} = workspacePlan(this); return JSON.parse(workspaceCall(state, "policy_preview", handle)); }
644
+ previews() { const {state, handle} = workspacePlan(this); return JSON.parse(workspaceCall(state, "previews", handle)); }
645
+ preview() { const {state, handle} = workspacePlan(this); return JSON.parse(workspaceCall(state, "preview", handle)); }
646
+ heads() { const {state, handle} = workspacePlan(this); return JSON.parse(workspaceCall(state, "plan_heads", handle)); }
647
+ exportCandidateBundle() { const {state, handle} = workspacePlan(this); return workspaceCall(state, "export_candidate_bundle", handle); }
648
+ discard() { const {state, handle} = workspacePlan(this); workspaceCall(state, "discard", handle); workspaceFinalizer.unregister(this); }
649
+ }
650
+
651
+ // The native child owns parser state. Transport copies cannot mutate caller buffers.
652
+ class WorkspaceRestoreStream {
653
+ #native;
654
+ constructor(native) { this.#native = native; Object.freeze(this); }
655
+ #call(method, ...args) {
656
+ try { const result = JSON.parse(this.#native[method](...args)); return result === null ? undefined : result; }
657
+ catch (error) { throw normalizeError(error, "internal_error"); }
658
+ }
659
+ feed(bytes) {
660
+ if (!(bytes instanceof Uint8Array)) return this.#call("reject_encoding");
661
+ let copied;
662
+ try { copied = Buffer.from(bytes); } catch { return this.#call("reject_encoding"); }
663
+ try { return this.#call("feed", copied); } finally { copied.fill(0); }
664
+ }
665
+ feedUtf16(units) {
666
+ if (!(units instanceof Uint16Array)) return this.#call("reject_encoding");
667
+ let copied;
668
+ try { copied = new Uint16Array(units); } catch { return this.#call("reject_encoding"); }
669
+ try { return this.#call("feed_utf16", copied); } finally { copied.fill(0); }
670
+ }
671
+ finalize() { return this.#call("finalize"); }
672
+ reset() { this.#call("reset"); }
673
+ close() { this.#native.close(); }
674
+ }
675
+
676
+ /** Provisional reversible-workspace API. Credentials and durable storage remain host-owned. */
677
+ export class Workspace {
678
+ #state;
679
+ constructor(token, native) {
680
+ if (token !== workspaceToken) throw workspaceFailure("invalid_configuration");
681
+ this.#state = { native, closed: false };
682
+ }
683
+ static create(namespace, operationId) {
684
+ workspaceBytes(namespace, 16, true); workspaceBytes(operationId, 16, true);
685
+ try { return new Workspace(workspaceToken, NativeWorkspace.create(Buffer.from(namespace), Buffer.from(operationId))); }
686
+ catch (error) { throw normalizeError(error, "internal_error"); }
687
+ }
688
+ static createWithOperation(namespace, context) {
689
+ workspaceBytes(namespace,16,true);
690
+ const operation=workspaceOperation(context);
691
+ try {return new Workspace(workspaceToken,NativeWorkspace.create_with_operation(Buffer.from(namespace),operation.json));}
692
+ catch(error){throw normalizeError(error,"internal_error");}
693
+ }
694
+ static openBundle(bytes) {
695
+ workspaceBytes(bytes, 16 * 1024 * 1024);
696
+ try { return new Workspace(workspaceToken, NativeWorkspace.open_bundle(Buffer.from(bytes))); }
697
+ catch (error) { throw normalizeError(error, "invalid_snapshot"); }
698
+ }
699
+ head() { return JSON.parse(workspaceCall(this.#state, "head")); }
700
+ namespace() { return workspaceCall(this.#state, "namespace"); }
701
+ exportBundle() { return workspaceCall(this.#state, "export_bundle"); }
702
+ operations() { return JSON.parse(workspaceCall(this.#state, "operations")); }
703
+ operationsPage(offset, limit) {
704
+ if (!Number.isSafeInteger(offset) || offset < 0 || offset > 100000 || !Number.isSafeInteger(limit) || limit < 1 || limit > 100000) throw workspaceFailure("resource_limit");
705
+ return JSON.parse(workspaceCall(this.#state, "operations_page", offset, limit));
706
+ }
707
+ counts() { return JSON.parse(workspaceCall(this.#state, "counts")); }
708
+ historyCoverage() { return JSON.parse(workspaceCall(this.#state, "history_coverage")); }
709
+ policy() { return JSON.parse(workspaceCall(this.#state,"policy")); }
710
+ preparePolicyChanges(edits, operationId) {
711
+ workspaceBytes(operationId,16,true);
712
+ let json;
713
+ try { json=workspacePolicyJson(edits); }
714
+ catch (error) { if (error instanceof DataFogError) throw error; throw workspaceFailure("invalid_policy"); }
715
+ return new PreparedWorkspaceChange(this.#state,workspaceCall(this.#state,"prepare_policy_changes",json,operationId));
716
+ }
717
+ recall(text) { workspaceText(text, 1024 * 1024); return JSON.parse(workspaceCall(this.#state, "recall", text)); }
718
+ /** Read-only review; returned candidates are never handoff permission. */
719
+ review(input) { return JSON.parse(workspaceCall(this.#state,"review",workspaceReviewJson(input))); }
720
+ prepareReview(inputs, operationId) {
721
+ workspaceBytes(operationId,16,true);
722
+ if(!Array.isArray(inputs) || inputs.length===0) throw workspaceFailure("invalid_batch");
723
+ if(inputs.length>32) throw workspaceFailure("resource_limit");
724
+ if(Reflect.ownKeys(inputs).length!==inputs.length+1) throw workspaceFailure("invalid_batch");
725
+ const parts=[];let budget=2;
726
+ for(let inputIndex=0;inputIndex<inputs.length;inputIndex++) {
727
+ try {
728
+ const item=Object.getOwnPropertyDescriptor(inputs,String(inputIndex));
729
+ if(!item?.enumerable || !("value" in item)) throw workspaceFailure("invalid_batch");
730
+ const part=workspaceReviewJson(item.value);budget+=new TextEncoder().encode(part).length+1;
731
+ if(budget>16*1024*1024) throw workspaceFailure("resource_limit");
732
+ parts.push(part);
733
+ } catch(error) { const failure=error instanceof DataFogError?error:workspaceFailure("invalid_finding");failure.inputIndex=inputIndex;throw failure; }
734
+ }
735
+ return new PreparedWorkspaceChange(this.#state,workspaceCall(this.#state,"prepare_review",`[${parts.join(",")}]`,operationId));
736
+ }
737
+ prepareConversationSend(inputs, operation) {
738
+ const context=workspaceOperation(operation);
739
+ if(!Array.isArray(inputs) || inputs.length===0 || Reflect.ownKeys(inputs).length!==inputs.length+1) throw workspaceFailure("invalid_batch");
740
+ if(inputs.length>32) throw workspaceFailure("resource_limit");
741
+ const parts=[];let budget=2;
742
+ for(let index=0;index<inputs.length;index++) {
743
+ try {
744
+ const item=Object.getOwnPropertyDescriptor(inputs,String(index));
745
+ if(!item?.enumerable || !("value" in item)) throw workspaceFailure("invalid_batch");
746
+ const row=conversationRecord(item.value,["input","conversation"]);
747
+ const part=`{"input":${workspaceReviewJson(row.input)},"conversation":${JSON.stringify(conversationInput(row.conversation))}}`;
748
+ budget+=new TextEncoder().encode(part).length+1;
749
+ if(budget>16*1024*1024) throw workspaceFailure("resource_limit");parts.push(part);
750
+ } catch(error) {const failure=error instanceof DataFogError?error:workspaceFailure("invalid_conversation");failure.inputIndex=index;throw failure;}
751
+ }
752
+ return new PreparedWorkspaceChange(this.#state,workspaceCall(this.#state,"prepare_conversation_send",`[${parts.join(",")}]`,context.json));
753
+ }
754
+ conversation(logicalId) {workspaceBytes(logicalId,16,true);return JSON.parse(workspaceCall(this.#state,"conversation",logicalId));}
755
+ conversationForSite(surface,siteId) {
756
+ conversationText(surface);conversationText(siteId);
757
+ return workspaceCall(this.#state,"conversation_for_site",surface,siteId);
758
+ }
759
+ sendIntentStatus(intent) {return JSON.parse(workspaceCall(this.#state,"send_intent_status",sendIntentJson(intent)));}
760
+ prepareObservedSend(intent,logicalId,surface,siteId,operation) {
761
+ workspaceBytes(logicalId,16,true);conversationText(surface);conversationText(siteId);
762
+ const context=workspaceOperation(operation);
763
+ return new PreparedWorkspaceChange(this.#state,workspaceCall(this.#state,"prepare_observed_send",sendIntentJson(intent),logicalId,surface,siteId,context.json));
764
+ }
765
+ prepareDefinitelyNotSent(intent,logicalId,operation) {
766
+ workspaceBytes(logicalId,16,true);const context=workspaceOperation(operation);
767
+ return new PreparedWorkspaceChange(this.#state,workspaceCall(this.#state,"prepare_definitely_not_sent",sendIntentJson(intent),logicalId,context.json));
768
+ }
769
+ restoreInConversation(text,logicalId) {workspaceText(text,16*1024*1024);workspaceBytes(logicalId,16,true);return workspaceCall(this.#state,"restore_in_conversation",text,logicalId);}
770
+ restoreReportInConversation(text,logicalId) {workspaceText(text,16*1024*1024);workspaceBytes(logicalId,16,true);return JSON.parse(workspaceCall(this.#state,"restore_report_in_conversation",text,logicalId));}
771
+ bufferedRestoreInConversation(logicalId,encoding="utf8") {
772
+ workspaceBytes(logicalId,16,true);if(encoding!=="utf8" && encoding!=="utf16") throw workspaceFailure("invalid_encoding");
773
+ return new WorkspaceRestoreStream(workspaceCall(this.#state,"buffered_restore_in_conversation",logicalId,encoding));
774
+ }
775
+ progressiveRestoreInConversation(logicalId,encoding="utf8") {
776
+ workspaceBytes(logicalId,16,true);if(encoding!=="utf8" && encoding!=="utf16") throw workspaceFailure("invalid_encoding");
777
+ return new WorkspaceRestoreStream(workspaceCall(this.#state,"progressive_restore_in_conversation",logicalId,encoding));
778
+ }
779
+ prepareRecovery(operationId) {
780
+ workspaceBytes(operationId, 16, true);
781
+ return new PreparedWorkspaceChange(this.#state, workspaceCall(this.#state, "prepare_recovery", operationId));
782
+ }
783
+ prepareRecoveryWithOperation(context) {
784
+ const operation = workspaceOperation(context);
785
+ return new PreparedWorkspaceChange(this.#state, workspaceCall(this.#state, "prepare_recovery", operation.id, operation.json));
786
+ }
787
+ prepareWithOperation(text, findings, context) { return this.prepareBatchWithOperation([{text, findings}], context); }
788
+ prepareOutcome(outcome, context) {
789
+ const operation = workspaceOperation(context);
790
+ if (!outcome || typeof outcome !== "object" || ![Object.prototype, null].includes(Object.getPrototypeOf(outcome))) throw workspaceFailure("invalid_operation");
791
+ const fields = Object.getOwnPropertyDescriptors(outcome);
792
+ if (Reflect.ownKeys(fields).length !== 2 || !fields.kind?.enumerable || !("value" in fields.kind) || !fields.issueCount?.enumerable || !("value" in fields.issueCount)) throw workspaceFailure("invalid_operation");
793
+ const kind = fields.kind.value, issueCount = fields.issueCount.value;
794
+ if (typeof kind !== "string" || kind.length > 32 || !Number.isSafeInteger(issueCount) || issueCount < 0 || issueCount > 10000) throw workspaceFailure("invalid_operation");
795
+ const json = JSON.stringify({kind, issueCount});
796
+ return new PreparedWorkspaceChange(this.#state, workspaceCall(this.#state, "prepare_outcome", json, operation.json));
797
+ }
798
+ prepare(text, findings, operationId) {
799
+ workspaceText(text, 1024 * 1024); workspaceBytes(operationId, 16, true);
800
+ if (!Array.isArray(findings)) throw workspaceFailure("invalid_finding");
801
+ if (findings.length > 10000) throw workspaceFailure("resource_limit");
802
+ let json;
803
+ try { json = workspaceFindingsJson(findings); }
804
+ catch (error) { if (error instanceof DataFogError) throw error; throw workspaceFailure("invalid_finding"); }
805
+ workspaceText(json, 16 * 1024 * 1024);
806
+ return new PreparedWorkspaceChange(this.#state, workspaceCall(this.#state, "prepare", text, json, operationId));
807
+ }
808
+ prepareBatch(inputs,operationId) {return this.#prepareBatch(inputs,operationId);}
809
+ prepareBatchWithOperation(inputs,context) {
810
+ const operation=workspaceOperation(context);
811
+ return this.#prepareBatch(inputs,operation.id,operation.json);
812
+ }
813
+ #prepareBatch(inputs, operationId, context) {
814
+ workspaceBytes(operationId, 16, true);
815
+ if (!Array.isArray(inputs) || inputs.length === 0) throw workspaceFailure("invalid_batch");
816
+ if (inputs.length > 32) throw workspaceFailure("resource_limit");
817
+ if (Reflect.ownKeys(inputs).length !== inputs.length + 1) throw workspaceFailure("invalid_batch");
818
+ const parts = [];
819
+ let sourceBytes = 0, findingCount = 0, transportBytes = 2;
820
+ for (let inputIndex = 0; inputIndex < inputs.length; inputIndex++) {
821
+ try {
822
+ const item = Object.getOwnPropertyDescriptor(inputs, String(inputIndex));
823
+ if (!item?.enumerable || !("value" in item)) throw workspaceFailure("invalid_batch");
824
+ const input = item.value;
825
+ if (!input || typeof input !== "object" || ![Object.prototype, null].includes(Object.getPrototypeOf(input))) throw workspaceFailure("invalid_batch");
826
+ const properties = Object.getOwnPropertyDescriptors(input);
827
+ if (Reflect.ownKeys(properties).length !== 2 || !properties.text?.enumerable || !("value" in properties.text) || !properties.findings?.enumerable || !("value" in properties.findings)) throw workspaceFailure("invalid_batch");
828
+ const {value: text} = properties.text, {value: findings} = properties.findings;
829
+ workspaceText(text, 1024 * 1024);
830
+ if (!Array.isArray(findings)) throw workspaceFailure("invalid_finding");
831
+ sourceBytes += new TextEncoder().encode(text).length;
832
+ findingCount += findings.length;
833
+ if (sourceBytes > 1024 * 1024 || findingCount > 10000) throw workspaceFailure("resource_limit");
834
+ const findingsJson = workspaceFindingsJson(findings);
835
+ const textJson = JSON.stringify(text);
836
+ transportBytes += 32 + new TextEncoder().encode(textJson).length + new TextEncoder().encode(findingsJson).length;
837
+ if (transportBytes > 16 * 1024 * 1024) throw workspaceFailure("resource_limit");
838
+ parts.push(`{"text":${textJson},"findings":${findingsJson}}`);
839
+ } catch (error) {
840
+ const failure = error instanceof DataFogError ? error : workspaceFailure("invalid_finding");
841
+ failure.inputIndex = inputIndex;
842
+ throw failure;
843
+ }
844
+ }
845
+ return new PreparedWorkspaceChange(this.#state, workspaceCall(this.#state, "prepare_batch", `[${parts.join(",")}]`, operationId, context));
846
+ }
847
+ activateSaved(change, expectedHead, savedHead) {
848
+ const plan = workspacePlans.get(change);
849
+ if (!plan || plan.state !== this.#state) throw workspaceFailure("workspace_conflict");
850
+ let expected, saved;
851
+ try { expected = structuredJson(expectedHead); saved = structuredJson(savedHead); }
852
+ catch { throw workspaceFailure("workspace_conflict"); }
853
+ workspaceCall(this.#state, "activate_saved", plan.handle, expected, saved);
854
+ workspaceFinalizer.unregister(change);
855
+ }
856
+ /** Strict buffered default: feeds release no text. */
857
+ bufferedRestore(encoding = "utf8") {
858
+ if (encoding !== "utf8" && encoding !== "utf16") throw workspaceFailure("invalid_encoding");
859
+ return new WorkspaceRestoreStream(workspaceCall(this.#state,"buffered_restore",encoding));
860
+ }
861
+ /** Explicit irreversible delivery; terminal failures preserve prior delivered counts. */
862
+ progressiveRestore(encoding = "utf8") {
863
+ if (encoding !== "utf8" && encoding !== "utf16") throw workspaceFailure("invalid_encoding");
864
+ return new WorkspaceRestoreStream(workspaceCall(this.#state,"progressive_restore",encoding));
865
+ }
866
+ restore(text) { workspaceText(text, 16 * 1024 * 1024); return workspaceCall(this.#state, "restore", text); }
867
+ restoreReport(text) { workspaceText(text, 16 * 1024 * 1024); return JSON.parse(workspaceCall(this.#state, "restore_report", text)); }
868
+ close() {
869
+ if (!this.#state.closed) {
870
+ workspaceCall(this.#state, "close");
871
+ this.#state.closed = true;
872
+ }
873
+ }
874
+ }
875
+
876
+ export function workspaceByteRangeFromUtf16(text, start, end) {
877
+ workspaceText(text, 1024 * 1024);
878
+ if (!Number.isSafeInteger(start) || !Number.isSafeInteger(end) || start < 0 || end < 0 || start > text.length || end > text.length) throw workspaceFailure("invalid_finding");
879
+ try { return JSON.parse(NativeWorkspace.byte_range_from_utf16(text, start, end)); }
880
+ catch (error) { throw normalizeError(error, "invalid_finding"); }
881
+ }
package/native.js CHANGED
@@ -81,8 +81,8 @@ function requireNative() {
81
81
  try {
82
82
  const binding = require('@datafog/node-android-arm64')
83
83
  const bindingPackageVersion = require('@datafog/node-android-arm64/package.json').version
84
- if (bindingPackageVersion !== '0.5.0-beta.2' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
85
- throw new Error(`Native binding package version mismatch, expected 0.5.0-beta.2 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
84
+ if (bindingPackageVersion !== '1.0.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
85
+ throw new Error(`Native binding package version mismatch, expected 1.0.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
86
86
  }
87
87
  return binding
88
88
  } catch (e) {
@@ -97,8 +97,8 @@ function requireNative() {
97
97
  try {
98
98
  const binding = require('@datafog/node-android-arm-eabi')
99
99
  const bindingPackageVersion = require('@datafog/node-android-arm-eabi/package.json').version
100
- if (bindingPackageVersion !== '0.5.0-beta.2' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
101
- throw new Error(`Native binding package version mismatch, expected 0.5.0-beta.2 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
100
+ if (bindingPackageVersion !== '1.0.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
101
+ throw new Error(`Native binding package version mismatch, expected 1.0.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
102
102
  }
103
103
  return binding
104
104
  } catch (e) {
@@ -118,8 +118,8 @@ function requireNative() {
118
118
  try {
119
119
  const binding = require('@datafog/node-win32-x64-gnu')
120
120
  const bindingPackageVersion = require('@datafog/node-win32-x64-gnu/package.json').version
121
- if (bindingPackageVersion !== '0.5.0-beta.2' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
122
- throw new Error(`Native binding package version mismatch, expected 0.5.0-beta.2 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
121
+ if (bindingPackageVersion !== '1.0.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
122
+ throw new Error(`Native binding package version mismatch, expected 1.0.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
123
123
  }
124
124
  return binding
125
125
  } catch (e) {
@@ -134,8 +134,8 @@ function requireNative() {
134
134
  try {
135
135
  const binding = require('@datafog/node-win32-x64-msvc')
136
136
  const bindingPackageVersion = require('@datafog/node-win32-x64-msvc/package.json').version
137
- if (bindingPackageVersion !== '0.5.0-beta.2' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
138
- throw new Error(`Native binding package version mismatch, expected 0.5.0-beta.2 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
137
+ if (bindingPackageVersion !== '1.0.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
138
+ throw new Error(`Native binding package version mismatch, expected 1.0.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
139
139
  }
140
140
  return binding
141
141
  } catch (e) {
@@ -151,8 +151,8 @@ function requireNative() {
151
151
  try {
152
152
  const binding = require('@datafog/node-win32-ia32-msvc')
153
153
  const bindingPackageVersion = require('@datafog/node-win32-ia32-msvc/package.json').version
154
- if (bindingPackageVersion !== '0.5.0-beta.2' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
155
- throw new Error(`Native binding package version mismatch, expected 0.5.0-beta.2 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
154
+ if (bindingPackageVersion !== '1.0.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
155
+ throw new Error(`Native binding package version mismatch, expected 1.0.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
156
156
  }
157
157
  return binding
158
158
  } catch (e) {
@@ -167,8 +167,8 @@ function requireNative() {
167
167
  try {
168
168
  const binding = require('@datafog/node-win32-arm64-msvc')
169
169
  const bindingPackageVersion = require('@datafog/node-win32-arm64-msvc/package.json').version
170
- if (bindingPackageVersion !== '0.5.0-beta.2' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
171
- throw new Error(`Native binding package version mismatch, expected 0.5.0-beta.2 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
170
+ if (bindingPackageVersion !== '1.0.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
171
+ throw new Error(`Native binding package version mismatch, expected 1.0.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
172
172
  }
173
173
  return binding
174
174
  } catch (e) {
@@ -186,8 +186,8 @@ function requireNative() {
186
186
  try {
187
187
  const binding = require('@datafog/node-darwin-universal')
188
188
  const bindingPackageVersion = require('@datafog/node-darwin-universal/package.json').version
189
- if (bindingPackageVersion !== '0.5.0-beta.2' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
190
- throw new Error(`Native binding package version mismatch, expected 0.5.0-beta.2 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
189
+ if (bindingPackageVersion !== '1.0.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
190
+ throw new Error(`Native binding package version mismatch, expected 1.0.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
191
191
  }
192
192
  return binding
193
193
  } catch (e) {
@@ -202,8 +202,8 @@ function requireNative() {
202
202
  try {
203
203
  const binding = require('@datafog/node-darwin-x64')
204
204
  const bindingPackageVersion = require('@datafog/node-darwin-x64/package.json').version
205
- if (bindingPackageVersion !== '0.5.0-beta.2' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
206
- throw new Error(`Native binding package version mismatch, expected 0.5.0-beta.2 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
205
+ if (bindingPackageVersion !== '1.0.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
206
+ throw new Error(`Native binding package version mismatch, expected 1.0.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
207
207
  }
208
208
  return binding
209
209
  } catch (e) {
@@ -218,8 +218,8 @@ function requireNative() {
218
218
  try {
219
219
  const binding = require('@datafog/node-darwin-arm64')
220
220
  const bindingPackageVersion = require('@datafog/node-darwin-arm64/package.json').version
221
- if (bindingPackageVersion !== '0.5.0-beta.2' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
222
- throw new Error(`Native binding package version mismatch, expected 0.5.0-beta.2 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
221
+ if (bindingPackageVersion !== '1.0.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
222
+ throw new Error(`Native binding package version mismatch, expected 1.0.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
223
223
  }
224
224
  return binding
225
225
  } catch (e) {
@@ -238,8 +238,8 @@ function requireNative() {
238
238
  try {
239
239
  const binding = require('@datafog/node-freebsd-x64')
240
240
  const bindingPackageVersion = require('@datafog/node-freebsd-x64/package.json').version
241
- if (bindingPackageVersion !== '0.5.0-beta.2' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
242
- throw new Error(`Native binding package version mismatch, expected 0.5.0-beta.2 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
241
+ if (bindingPackageVersion !== '1.0.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
242
+ throw new Error(`Native binding package version mismatch, expected 1.0.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
243
243
  }
244
244
  return binding
245
245
  } catch (e) {
@@ -254,8 +254,8 @@ function requireNative() {
254
254
  try {
255
255
  const binding = require('@datafog/node-freebsd-arm64')
256
256
  const bindingPackageVersion = require('@datafog/node-freebsd-arm64/package.json').version
257
- if (bindingPackageVersion !== '0.5.0-beta.2' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
258
- throw new Error(`Native binding package version mismatch, expected 0.5.0-beta.2 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
257
+ if (bindingPackageVersion !== '1.0.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
258
+ throw new Error(`Native binding package version mismatch, expected 1.0.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
259
259
  }
260
260
  return binding
261
261
  } catch (e) {
@@ -275,8 +275,8 @@ function requireNative() {
275
275
  try {
276
276
  const binding = require('@datafog/node-linux-x64-musl')
277
277
  const bindingPackageVersion = require('@datafog/node-linux-x64-musl/package.json').version
278
- if (bindingPackageVersion !== '0.5.0-beta.2' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
279
- throw new Error(`Native binding package version mismatch, expected 0.5.0-beta.2 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
278
+ if (bindingPackageVersion !== '1.0.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
279
+ throw new Error(`Native binding package version mismatch, expected 1.0.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
280
280
  }
281
281
  return binding
282
282
  } catch (e) {
@@ -291,8 +291,8 @@ function requireNative() {
291
291
  try {
292
292
  const binding = require('@datafog/node-linux-x64-gnu')
293
293
  const bindingPackageVersion = require('@datafog/node-linux-x64-gnu/package.json').version
294
- if (bindingPackageVersion !== '0.5.0-beta.2' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
295
- throw new Error(`Native binding package version mismatch, expected 0.5.0-beta.2 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
294
+ if (bindingPackageVersion !== '1.0.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
295
+ throw new Error(`Native binding package version mismatch, expected 1.0.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
296
296
  }
297
297
  return binding
298
298
  } catch (e) {
@@ -309,8 +309,8 @@ function requireNative() {
309
309
  try {
310
310
  const binding = require('@datafog/node-linux-arm64-musl')
311
311
  const bindingPackageVersion = require('@datafog/node-linux-arm64-musl/package.json').version
312
- if (bindingPackageVersion !== '0.5.0-beta.2' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
313
- throw new Error(`Native binding package version mismatch, expected 0.5.0-beta.2 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
312
+ if (bindingPackageVersion !== '1.0.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
313
+ throw new Error(`Native binding package version mismatch, expected 1.0.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
314
314
  }
315
315
  return binding
316
316
  } catch (e) {
@@ -325,8 +325,8 @@ function requireNative() {
325
325
  try {
326
326
  const binding = require('@datafog/node-linux-arm64-gnu')
327
327
  const bindingPackageVersion = require('@datafog/node-linux-arm64-gnu/package.json').version
328
- if (bindingPackageVersion !== '0.5.0-beta.2' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
329
- throw new Error(`Native binding package version mismatch, expected 0.5.0-beta.2 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
328
+ if (bindingPackageVersion !== '1.0.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
329
+ throw new Error(`Native binding package version mismatch, expected 1.0.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
330
330
  }
331
331
  return binding
332
332
  } catch (e) {
@@ -343,8 +343,8 @@ function requireNative() {
343
343
  try {
344
344
  const binding = require('@datafog/node-linux-arm-musleabihf')
345
345
  const bindingPackageVersion = require('@datafog/node-linux-arm-musleabihf/package.json').version
346
- if (bindingPackageVersion !== '0.5.0-beta.2' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
347
- throw new Error(`Native binding package version mismatch, expected 0.5.0-beta.2 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
346
+ if (bindingPackageVersion !== '1.0.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
347
+ throw new Error(`Native binding package version mismatch, expected 1.0.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
348
348
  }
349
349
  return binding
350
350
  } catch (e) {
@@ -359,8 +359,8 @@ function requireNative() {
359
359
  try {
360
360
  const binding = require('@datafog/node-linux-arm-gnueabihf')
361
361
  const bindingPackageVersion = require('@datafog/node-linux-arm-gnueabihf/package.json').version
362
- if (bindingPackageVersion !== '0.5.0-beta.2' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
363
- throw new Error(`Native binding package version mismatch, expected 0.5.0-beta.2 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
362
+ if (bindingPackageVersion !== '1.0.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
363
+ throw new Error(`Native binding package version mismatch, expected 1.0.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
364
364
  }
365
365
  return binding
366
366
  } catch (e) {
@@ -377,8 +377,8 @@ function requireNative() {
377
377
  try {
378
378
  const binding = require('@datafog/node-linux-loong64-musl')
379
379
  const bindingPackageVersion = require('@datafog/node-linux-loong64-musl/package.json').version
380
- if (bindingPackageVersion !== '0.5.0-beta.2' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
381
- throw new Error(`Native binding package version mismatch, expected 0.5.0-beta.2 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
380
+ if (bindingPackageVersion !== '1.0.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
381
+ throw new Error(`Native binding package version mismatch, expected 1.0.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
382
382
  }
383
383
  return binding
384
384
  } catch (e) {
@@ -393,8 +393,8 @@ function requireNative() {
393
393
  try {
394
394
  const binding = require('@datafog/node-linux-loong64-gnu')
395
395
  const bindingPackageVersion = require('@datafog/node-linux-loong64-gnu/package.json').version
396
- if (bindingPackageVersion !== '0.5.0-beta.2' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
397
- throw new Error(`Native binding package version mismatch, expected 0.5.0-beta.2 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
396
+ if (bindingPackageVersion !== '1.0.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
397
+ throw new Error(`Native binding package version mismatch, expected 1.0.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
398
398
  }
399
399
  return binding
400
400
  } catch (e) {
@@ -411,8 +411,8 @@ function requireNative() {
411
411
  try {
412
412
  const binding = require('@datafog/node-linux-riscv64-musl')
413
413
  const bindingPackageVersion = require('@datafog/node-linux-riscv64-musl/package.json').version
414
- if (bindingPackageVersion !== '0.5.0-beta.2' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
415
- throw new Error(`Native binding package version mismatch, expected 0.5.0-beta.2 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
414
+ if (bindingPackageVersion !== '1.0.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
415
+ throw new Error(`Native binding package version mismatch, expected 1.0.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
416
416
  }
417
417
  return binding
418
418
  } catch (e) {
@@ -427,8 +427,8 @@ function requireNative() {
427
427
  try {
428
428
  const binding = require('@datafog/node-linux-riscv64-gnu')
429
429
  const bindingPackageVersion = require('@datafog/node-linux-riscv64-gnu/package.json').version
430
- if (bindingPackageVersion !== '0.5.0-beta.2' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
431
- throw new Error(`Native binding package version mismatch, expected 0.5.0-beta.2 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
430
+ if (bindingPackageVersion !== '1.0.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
431
+ throw new Error(`Native binding package version mismatch, expected 1.0.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
432
432
  }
433
433
  return binding
434
434
  } catch (e) {
@@ -444,8 +444,8 @@ function requireNative() {
444
444
  try {
445
445
  const binding = require('@datafog/node-linux-ppc64-gnu')
446
446
  const bindingPackageVersion = require('@datafog/node-linux-ppc64-gnu/package.json').version
447
- if (bindingPackageVersion !== '0.5.0-beta.2' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
448
- throw new Error(`Native binding package version mismatch, expected 0.5.0-beta.2 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
447
+ if (bindingPackageVersion !== '1.0.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
448
+ throw new Error(`Native binding package version mismatch, expected 1.0.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
449
449
  }
450
450
  return binding
451
451
  } catch (e) {
@@ -460,8 +460,8 @@ function requireNative() {
460
460
  try {
461
461
  const binding = require('@datafog/node-linux-s390x-gnu')
462
462
  const bindingPackageVersion = require('@datafog/node-linux-s390x-gnu/package.json').version
463
- if (bindingPackageVersion !== '0.5.0-beta.2' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
464
- throw new Error(`Native binding package version mismatch, expected 0.5.0-beta.2 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
463
+ if (bindingPackageVersion !== '1.0.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
464
+ throw new Error(`Native binding package version mismatch, expected 1.0.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
465
465
  }
466
466
  return binding
467
467
  } catch (e) {
@@ -480,8 +480,8 @@ function requireNative() {
480
480
  try {
481
481
  const binding = require('@datafog/node-openharmony-arm64')
482
482
  const bindingPackageVersion = require('@datafog/node-openharmony-arm64/package.json').version
483
- if (bindingPackageVersion !== '0.5.0-beta.2' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
484
- throw new Error(`Native binding package version mismatch, expected 0.5.0-beta.2 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
483
+ if (bindingPackageVersion !== '1.0.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
484
+ throw new Error(`Native binding package version mismatch, expected 1.0.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
485
485
  }
486
486
  return binding
487
487
  } catch (e) {
@@ -496,8 +496,8 @@ function requireNative() {
496
496
  try {
497
497
  const binding = require('@datafog/node-openharmony-x64')
498
498
  const bindingPackageVersion = require('@datafog/node-openharmony-x64/package.json').version
499
- if (bindingPackageVersion !== '0.5.0-beta.2' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
500
- throw new Error(`Native binding package version mismatch, expected 0.5.0-beta.2 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
499
+ if (bindingPackageVersion !== '1.0.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
500
+ throw new Error(`Native binding package version mismatch, expected 1.0.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
501
501
  }
502
502
  return binding
503
503
  } catch (e) {
@@ -512,8 +512,8 @@ function requireNative() {
512
512
  try {
513
513
  const binding = require('@datafog/node-openharmony-arm')
514
514
  const bindingPackageVersion = require('@datafog/node-openharmony-arm/package.json').version
515
- if (bindingPackageVersion !== '0.5.0-beta.2' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
516
- throw new Error(`Native binding package version mismatch, expected 0.5.0-beta.2 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
515
+ if (bindingPackageVersion !== '1.0.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
516
+ throw new Error(`Native binding package version mismatch, expected 1.0.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
517
517
  }
518
518
  return binding
519
519
  } catch (e) {
@@ -652,8 +652,8 @@ if (!nativeBinding || forceWasi) {
652
652
  if (!candidateFailed) {
653
653
  if (process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
654
654
  const bindingPackageVersion = require('@datafog/node-wasm32-wasi/package.json').version
655
- if (bindingPackageVersion !== '0.5.0-beta.2') {
656
- throw new Error(`WASI binding package version mismatch, expected 0.5.0-beta.2 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
655
+ if (bindingPackageVersion !== '1.0.0') {
656
+ throw new Error(`WASI binding package version mismatch, expected 1.0.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
657
657
  }
658
658
  }
659
659
  wasiBinding = require('@datafog/node-wasm32-wasi')
@@ -703,9 +703,11 @@ if (!nativeBinding) {
703
703
  throw new Error(`Failed to load native binding`)
704
704
  }
705
705
 
706
- const { ModelCancellation, ModelSessionNative, discoverFields, openModelSession, prepareScanAndTransform, prepareStructuredScanAndTransform, requiredKeySelectors, requiredRestoreItems, requiredTokenizationItems, restoreWithResults, scan, scanAndTransform, scanStructured, structuredRequiredKeySelectors, structuredRequiredRestoreItems, structuredRequiredTokenizationItems, structuredRestoreWithResults, structuredScanAndTransform, structuredTransform, structuredTransformWithProviderResults, transform, transformWithProviderResults, transformWithResolvedKeys } = nativeBinding
706
+ const { ModelCancellation, ModelSessionNative, NativeRestoreStream, NativeWorkspace, discoverFields, openModelSession, prepareScanAndTransform, prepareStructuredScanAndTransform, requiredKeySelectors, requiredRestoreItems, requiredTokenizationItems, restoreWithResults, scan, scanAndTransform, scanStructured, structuredRequiredKeySelectors, structuredRequiredRestoreItems, structuredRequiredTokenizationItems, structuredRestoreWithResults, structuredScanAndTransform, structuredTransform, structuredTransformWithProviderResults, transform, transformWithProviderResults, transformWithResolvedKeys, workspaceContractVersion } = nativeBinding
707
707
  export { ModelCancellation }
708
708
  export { ModelSessionNative }
709
+ export { NativeRestoreStream }
710
+ export { NativeWorkspace }
709
711
  export { discoverFields }
710
712
  export { openModelSession }
711
713
  export { prepareScanAndTransform }
@@ -727,3 +729,4 @@ export { structuredTransformWithProviderResults }
727
729
  export { transform }
728
730
  export { transformWithProviderResults }
729
731
  export { transformWithResolvedKeys }
732
+ export { workspaceContractVersion }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@datafog/node",
3
- "version": "0.5.0-beta.2",
3
+ "version": "1.0.0",
4
4
  "description": "Fast structured PII detection for Node.js, powered by Rust",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -9,7 +9,7 @@
9
9
  },
10
10
  "publishConfig": {
11
11
  "access": "public",
12
- "tag": "beta"
12
+ "tag": "latest"
13
13
  },
14
14
  "type": "module",
15
15
  "files": [
@@ -26,7 +26,7 @@
26
26
  },
27
27
  "types": "./index.d.ts",
28
28
  "engines": {
29
- "node": "24.x"
29
+ "node": ">=24"
30
30
  },
31
31
  "napi": {
32
32
  "binaryName": "datafog",
@@ -41,6 +41,7 @@
41
41
  },
42
42
  "scripts": {
43
43
  "build": "napi build --manifest-path Cargo.toml --release --platform --esm --js native.js --dts index.d.ts",
44
+ "postbuild": "node finalize-types.mjs",
44
45
  "test:package": "node ../../scripts/test-node-package.mjs",
45
46
  "prepublishOnly": "napi prepublish -t npm --no-gh-release"
46
47
  },
@@ -49,10 +50,10 @@
49
50
  "typescript": "7.0.2"
50
51
  },
51
52
  "optionalDependencies": {
52
- "@datafog/node-darwin-arm64": "0.5.0-beta.2",
53
- "@datafog/node-darwin-x64": "0.5.0-beta.2",
54
- "@datafog/node-linux-x64-gnu": "0.5.0-beta.2",
55
- "@datafog/node-linux-arm64-gnu": "0.5.0-beta.2",
56
- "@datafog/node-win32-x64-msvc": "0.5.0-beta.2"
53
+ "@datafog/node-darwin-arm64": "1.0.0",
54
+ "@datafog/node-darwin-x64": "1.0.0",
55
+ "@datafog/node-linux-x64-gnu": "1.0.0",
56
+ "@datafog/node-linux-arm64-gnu": "1.0.0",
57
+ "@datafog/node-win32-x64-msvc": "1.0.0"
57
58
  }
58
59
  }