@orkestrel/workspace 0.0.5 → 0.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.
package/README.md CHANGED
@@ -41,7 +41,7 @@ Ranged edits clamp to the text they address, search is literal until you ask for
41
41
  absent path answers with `undefined` instead of throwing. Give the manager a store and `save` and
42
42
  `open` round-trip a workspace through it.
43
43
 
44
- See the [workspace guide](guides/src/workspace.md) for the complete surface, edit semantics,
44
+ See the [workspace guide](guides/workspace.md) for the complete surface, edit semantics,
45
45
  events, and stores.
46
46
 
47
47
  ## Requirements
@@ -4,7 +4,7 @@ let _orkestrel_database = require("@orkestrel/database");
4
4
  let _orkestrel_emitter = require("@orkestrel/emitter");
5
5
  //#region src/core/constants.ts
6
6
  /**
7
- * File extensions mapped to language tags for text content.
7
+ * Maps file extensions to language tags for text content.
8
8
  *
9
9
  * Unknown extensions intentionally fall back to `text` in
10
10
  * {@link import('./helpers.js').inferLanguage}.
@@ -53,12 +53,12 @@ var EXTENSION_LANGUAGES = Object.freeze({
53
53
  });
54
54
  //#endregion
55
55
  //#region src/core/errors.ts
56
- /** An invalid workspace edit or search operation. */
56
+ /** Reports an invalid workspace edit or search operation. */
57
57
  var WorkspaceError = class extends Error {
58
58
  code;
59
59
  context;
60
60
  /**
61
- * Create a workspace error.
61
+ * Creates a workspace error.
62
62
  *
63
63
  * @param code - The machine-readable failure code
64
64
  * @param message - The human-readable failure message
@@ -72,10 +72,10 @@ var WorkspaceError = class extends Error {
72
72
  }
73
73
  };
74
74
  /**
75
- * Narrow a caught value to a {@link WorkspaceError}.
75
+ * Narrows a caught value to a {@link WorkspaceError}.
76
76
  *
77
77
  * @param value - The caught value
78
- * @returns Whether the value is a workspace error
78
+ * @returns True if the value is a workspace error; false otherwise
79
79
  *
80
80
  * @example
81
81
  * ```ts
@@ -88,7 +88,7 @@ function isWorkspaceError(value) {
88
88
  //#endregion
89
89
  //#region src/core/helpers.ts
90
90
  /**
91
- * Infer a language tag from the final file extension.
91
+ * Infers a language tag from the final file extension.
92
92
  *
93
93
  * @param path - The file path
94
94
  * @returns The mapped language, or `text` when the extension is absent or unknown
@@ -104,10 +104,10 @@ function inferLanguage(path) {
104
104
  return EXTENSION_LANGUAGES[path.slice(dot + 1).toLowerCase()] ?? "text";
105
105
  }
106
106
  /**
107
- * Determine whether content is the text arm.
107
+ * Determines whether content is the text arm.
108
108
  *
109
109
  * @param content - The file content
110
- * @returns Whether the content carries text and a language
110
+ * @returns True if the content carries text and a language; false otherwise
111
111
  *
112
112
  * @example
113
113
  * ```ts
@@ -118,57 +118,21 @@ function isText(content) {
118
118
  return "text" in content;
119
119
  }
120
120
  /**
121
- * Determine whether content is the binary arm.
121
+ * Checks whether content is the binary arm.
122
122
  *
123
123
  * @param content - The file content
124
- * @returns Whether the content carries base64 data and a MIME
124
+ * @returns True if the content carries a base64 string and a MIME; false otherwise
125
125
  *
126
126
  * @example
127
127
  * ```ts
128
- * isBinary({ data: 'AAAA', mime: 'image/png' }) // true
128
+ * isBinary({ base64: 'AAAA', mime: 'image/png' }) // true
129
129
  * ```
130
130
  */
131
131
  function isBinary(content) {
132
- return "data" in content;
132
+ return "base64" in content;
133
133
  }
134
134
  /**
135
- * Narrow an unknown value to an immutable file record.
136
- *
137
- * @param value - The value to inspect
138
- * @returns Whether the value has the file shape
139
- *
140
- * @example
141
- * ```ts
142
- * isFile({ path: 'a.txt', content: { text: 'a', language: 'text' }, state: 'created', size: 1, lines: 1 })
143
- * ```
144
- */
145
- function isFile(value) {
146
- return (0, _orkestrel_contract.holds)(() => {
147
- if (!(0, _orkestrel_contract.isRecord)(value)) return false;
148
- if (!(0, _orkestrel_contract.isString)(value.path) || !(0, _orkestrel_contract.literalOf)("created", "modified")(value.state)) return false;
149
- if (!(0, _orkestrel_contract.isNumber)(value.size) || !(0, _orkestrel_contract.isNumber)(value.lines)) return false;
150
- if (!(0, _orkestrel_contract.isRecord)(value.content)) return false;
151
- const text = (0, _orkestrel_contract.isString)(value.content.text) && (0, _orkestrel_contract.isString)(value.content.language);
152
- const binary = (0, _orkestrel_contract.isString)(value.content.data) && (0, _orkestrel_contract.literalOf)("image/png", "image/jpeg", "image/gif", "image/webp")(value.content.mime);
153
- return text || binary;
154
- });
155
- }
156
- /**
157
- * Narrow an unknown value to a workspace snapshot.
158
- *
159
- * @param value - The value read from a persistence boundary
160
- * @returns Whether the value has the workspace snapshot shape
161
- *
162
- * @example
163
- * ```ts
164
- * isWorkspaceSnapshot({ id: 'work', files: [] }) // true
165
- * ```
166
- */
167
- function isWorkspaceSnapshot(value) {
168
- return (0, _orkestrel_contract.holds)(() => (0, _orkestrel_contract.isRecord)(value) && (0, _orkestrel_contract.isString)(value.id) && (0, _orkestrel_contract.arrayOf)(isFile)(value.files));
169
- }
170
- /**
171
- * Compute the byte size of file content.
135
+ * Computes the byte size of file content.
172
136
  *
173
137
  * @param content - The content to measure
174
138
  * @returns UTF-8 bytes for text or decoded bytes for binary content
@@ -180,10 +144,10 @@ function isWorkspaceSnapshot(value) {
180
144
  */
181
145
  function computeSize(content) {
182
146
  if (isText(content)) return new TextEncoder().encode(content.text).length;
183
- return decodedSize(content.data);
147
+ return computeDecodedSize(content.base64);
184
148
  }
185
149
  /**
186
- * Count the lines in file content.
150
+ * Counts the lines in file content.
187
151
  *
188
152
  * @param content - The content to inspect
189
153
  * @returns The text line count, or zero for empty text and binary content
@@ -201,27 +165,27 @@ function countLines(content) {
201
165
  return count;
202
166
  }
203
167
  /**
204
- * Compute the decoded byte length of a base64 string.
168
+ * Computes the decoded byte length of a base64 string.
205
169
  *
206
- * @param base64 - The base64 data
170
+ * @param base64 - The base64 string
207
171
  * @returns The decoded byte count
208
172
  *
209
173
  * @example
210
174
  * ```ts
211
- * decodedSize('AAAA') // 3
175
+ * computeDecodedSize('AAAA') // 3
212
176
  * ```
213
177
  */
214
- function decodedSize(base64) {
178
+ function computeDecodedSize(base64) {
215
179
  let padding = 0;
216
180
  if (base64.endsWith("==")) padding = 2;
217
181
  else if (base64.endsWith("=")) padding = 1;
218
182
  return Math.floor(base64.length * 3 / 4) - padding;
219
183
  }
220
184
  /**
221
- * Determine whether a 1-based range is structurally valid.
185
+ * Determines whether a 1-based range is structurally valid.
222
186
  *
223
187
  * @param range - The range to inspect
224
- * @returns Whether both positions are positive and ordered
188
+ * @returns True if both positions are positive and ordered; false otherwise
225
189
  *
226
190
  * @example
227
191
  * ```ts
@@ -235,7 +199,7 @@ function isValidRange(range) {
235
199
  return !(range.start.line === range.end.line && range.start.column > range.end.column);
236
200
  }
237
201
  /**
238
- * Clamp a position to text bounds.
202
+ * Clamps a position to text bounds.
239
203
  *
240
204
  * @param text - The addressed text
241
205
  * @param position - The requested position
@@ -256,7 +220,7 @@ function clampPosition(text, position) {
256
220
  };
257
221
  }
258
222
  /**
259
- * Clamp both positions in a range to text bounds.
223
+ * Clamps both positions in a range to text bounds.
260
224
  *
261
225
  * @param text - The addressed text
262
226
  * @param range - The requested range
@@ -274,7 +238,7 @@ function clampRange(text, range) {
274
238
  };
275
239
  }
276
240
  /**
277
- * Convert a 1-based position to a zero-based string offset.
241
+ * Converts a 1-based position to a zero-based string offset.
278
242
  *
279
243
  * @param text - The addressed text
280
244
  * @param position - The requested position
@@ -293,7 +257,7 @@ function offsetAt(text, position) {
293
257
  return Math.min(offset, text.length);
294
258
  }
295
259
  /**
296
- * Slice a clamped half-open text range.
260
+ * Slices a clamped half-open text range.
297
261
  *
298
262
  * @param text - The source text
299
263
  * @param range - The requested range
@@ -309,7 +273,7 @@ function sliceRange(text, range) {
309
273
  return text.slice(offsetAt(text, clamped.start), offsetAt(text, clamped.end));
310
274
  }
311
275
  /**
312
- * Replace a clamped half-open text range.
276
+ * Replaces a clamped half-open text range.
313
277
  *
314
278
  * @param text - The source text
315
279
  * @param range - The requested range
@@ -328,7 +292,7 @@ function spliceRange(text, range, replacement) {
328
292
  return text.slice(0, start) + replacement + text.slice(end);
329
293
  }
330
294
  /**
331
- * Assemble a nested range from four 1-based coordinates.
295
+ * Assembles a nested range from four 1-based coordinates.
332
296
  *
333
297
  * @param fromLine - The start line
334
298
  * @param fromColumn - The start column
@@ -354,7 +318,7 @@ function rangeOf(fromLine, fromColumn, toLine, toColumn) {
354
318
  };
355
319
  }
356
320
  /**
357
- * Escape regular-expression metacharacters for literal matching.
321
+ * Escapes regular-expression metacharacters for literal matching.
358
322
  *
359
323
  * @param value - The literal text
360
324
  * @returns The escaped regular-expression source
@@ -368,9 +332,47 @@ function escapeRegExp(value) {
368
332
  return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
369
333
  }
370
334
  //#endregion
335
+ //#region src/core/validators.ts
336
+ /**
337
+ * Narrows an unknown value to an immutable file record.
338
+ *
339
+ * @param value - The value to inspect
340
+ * @returns True if the value has the file shape; false otherwise
341
+ *
342
+ * @example
343
+ * ```ts
344
+ * isFile({ path: 'a.txt', content: { text: 'a', language: 'text' }, state: 'created', size: 1, lines: 1 })
345
+ * ```
346
+ */
347
+ function isFile(value) {
348
+ return (0, _orkestrel_contract.holds)(() => {
349
+ if (!(0, _orkestrel_contract.isRecord)(value)) return false;
350
+ if (!(0, _orkestrel_contract.isString)(value.path) || !(0, _orkestrel_contract.literalOf)("created", "modified")(value.state)) return false;
351
+ if (!(0, _orkestrel_contract.isNumber)(value.size) || !(0, _orkestrel_contract.isNumber)(value.lines)) return false;
352
+ if (!(0, _orkestrel_contract.isRecord)(value.content)) return false;
353
+ const text = (0, _orkestrel_contract.isString)(value.content.text) && (0, _orkestrel_contract.isString)(value.content.language);
354
+ const binary = (0, _orkestrel_contract.isString)(value.content.base64) && (0, _orkestrel_contract.literalOf)("image/png", "image/jpeg", "image/gif", "image/webp")(value.content.mime);
355
+ return text || binary;
356
+ });
357
+ }
358
+ /**
359
+ * Narrows an unknown value to a workspace snapshot.
360
+ *
361
+ * @param value - The value read from a persistence boundary
362
+ * @returns True if the value has the workspace snapshot shape; false otherwise
363
+ *
364
+ * @example
365
+ * ```ts
366
+ * isWorkspaceSnapshot({ id: 'work', files: [] }) // true
367
+ * ```
368
+ */
369
+ function isWorkspaceSnapshot(value) {
370
+ return (0, _orkestrel_contract.holds)(() => (0, _orkestrel_contract.isRecord)(value) && (0, _orkestrel_contract.isString)(value.id) && (0, _orkestrel_contract.arrayOf)(isFile)(value.files));
371
+ }
372
+ //#endregion
371
373
  //#region src/core/workspaces/Workspace.ts
372
374
  /**
373
- * A mutable path-keyed editing surface over immutable files.
375
+ * Provides a mutable path-keyed editing surface over immutable files.
374
376
  *
375
377
  * Whole-file edits create text files, ranged edits operate only on existing text files, and
376
378
  * binary files remain available through construction-time hydration. Mutations emit after the
@@ -391,7 +393,7 @@ var Workspace = class {
391
393
  #files = /* @__PURE__ */ new Map();
392
394
  #emitter;
393
395
  /**
394
- * Create a workspace.
396
+ * Creates a workspace.
395
397
  *
396
398
  * @param options - Optional identity, emitter configuration, and initial files
397
399
  */
@@ -437,7 +439,7 @@ var Workspace = class {
437
439
  };
438
440
  }
439
441
  has(path) {
440
- if ((0, _orkestrel_contract.isArray)(path)) return path.some((one) => this.#files.has(one));
442
+ if ((0, _orkestrel_contract.isArray)(path)) return path.every((one) => this.#files.has(one));
441
443
  return this.#files.has(path);
442
444
  }
443
445
  search(query, options) {
@@ -447,7 +449,7 @@ var Workspace = class {
447
449
  for (const file of this.#files.values()) {
448
450
  if (limit !== void 0 && matches.length >= limit) break;
449
451
  if (!isText(file.content)) continue;
450
- const lines = file.content.text.split("\n");
452
+ const lines = file.content.text.split(/\r\n|\n/);
451
453
  for (let index = 0; index < lines.length; index += 1) {
452
454
  if (limit !== void 0 && matches.length >= limit) break;
453
455
  const lineText = lines[index] ?? "";
@@ -501,39 +503,42 @@ var Workspace = class {
501
503
  for (const [one, text] of Object.entries(path)) this.#write(one, text);
502
504
  return;
503
505
  }
504
- const text = content ?? "";
506
+ if (content === void 0) return;
505
507
  if (!range) {
506
- this.#write(path, text);
508
+ this.#write(path, content);
507
509
  return;
508
510
  }
509
- this.#splice(path, text, range);
511
+ this.#splice(path, content, range);
510
512
  }
511
513
  prepend(path, content) {
512
514
  if ((0, _orkestrel_contract.isRecord)(path)) {
513
515
  for (const [one, text] of Object.entries(path)) this.#prepend(one, text);
514
516
  return;
515
517
  }
516
- this.#prepend(path, content ?? "");
518
+ if (content === void 0) return;
519
+ this.#prepend(path, content);
517
520
  }
518
521
  append(path, content) {
519
522
  if ((0, _orkestrel_contract.isRecord)(path)) {
520
523
  for (const [one, text] of Object.entries(path)) this.#append(one, text);
521
524
  return;
522
525
  }
523
- this.#append(path, content ?? "");
526
+ if (content === void 0) return;
527
+ this.#append(path, content);
524
528
  }
525
529
  move(from, to) {
526
530
  if ((0, _orkestrel_contract.isRecord)(from)) {
527
- let moved = false;
528
- for (const [one, target] of Object.entries(from)) if (this.#move(one, target)) moved = true;
531
+ let moved = true;
532
+ for (const [one, target] of Object.entries(from)) if (!this.#move(one, target)) moved = false;
529
533
  return moved;
530
534
  }
531
- return this.#move(from, to ?? "");
535
+ if (to === void 0) return false;
536
+ return this.#move(from, to);
532
537
  }
533
538
  remove(path) {
534
539
  if ((0, _orkestrel_contract.isArray)(path)) {
535
- let removed = false;
536
- for (const one of path) if (this.#remove(one)) removed = true;
540
+ let removed = true;
541
+ for (const one of path) if (!this.#remove(one)) removed = false;
537
542
  return removed;
538
543
  }
539
544
  return this.#remove(path);
@@ -566,7 +571,7 @@ var Workspace = class {
566
571
  }
567
572
  #splice(path, content, range) {
568
573
  const existing = this.#files.get(path);
569
- if (!existing) throw new WorkspaceError("MODALITY", `Cannot splice a range of a missing file: ${path}`, { path });
574
+ if (!existing) throw new WorkspaceError("MISSING", `Cannot splice a range of a missing file: ${path}`, { path });
570
575
  if (!isText(existing.content)) throw new WorkspaceError("MODALITY", `Cannot splice a range of a binary file: ${path}`, { path });
571
576
  if (!isValidRange(range)) throw new WorkspaceError("RANGE", `Invalid range for file: ${path}`, {
572
577
  path,
@@ -635,7 +640,7 @@ var Workspace = class {
635
640
  //#endregion
636
641
  //#region src/core/workspaces/WorkspaceManager.ts
637
642
  /**
638
- * An insertion-ordered workspace registry with an active selection.
643
+ * Provides an insertion-ordered workspace registry with an active selection.
639
644
  *
640
645
  * A supplied store adds lenient snapshot `open` and `save` operations. Event defaults flow into
641
646
  * workspaces created through the registry, while observability remains owned by each workspace.
@@ -656,7 +661,7 @@ var WorkspaceManager = class {
656
661
  #error;
657
662
  #store;
658
663
  /**
659
- * Create a registry.
664
+ * Creates a registry.
660
665
  *
661
666
  * @param options - Default event hooks and optional snapshot store
662
667
  */
@@ -720,8 +725,8 @@ var WorkspaceManager = class {
720
725
  }
721
726
  remove(ids) {
722
727
  if ((0, _orkestrel_contract.isArray)(ids)) {
723
- let removed = false;
724
- for (const id of ids) if (this.#drop(id)) removed = true;
728
+ let removed = true;
729
+ for (const id of ids) if (!this.#drop(id)) removed = false;
725
730
  return removed;
726
731
  }
727
732
  return this.#drop(ids);
@@ -743,21 +748,27 @@ var WorkspaceManager = class {
743
748
  //#endregion
744
749
  //#region src/core/workspaces/stores/DatabaseWorkspaceStore.ts
745
750
  /**
746
- * A workspace snapshot store over a database table.
751
+ * Persists workspace snapshots in a database table.
747
752
  *
748
753
  * Snapshots occupy one opaque column and are narrowed when read back from the storage boundary.
749
754
  *
750
755
  * @example
751
756
  * ```ts
752
- * import { createDatabaseWorkspaceStore } from '@orkestrel/workspace'
753
- *
754
- * const store = createDatabaseWorkspaceStore()
757
+ * import { rawShape, stringShape } from '@orkestrel/contract'
758
+ * import { createDatabase, createMemoryDriver } from '@orkestrel/database'
759
+ * import { DatabaseWorkspaceStore } from '@orkestrel/workspace'
760
+ *
761
+ * const database = createDatabase({
762
+ * driver: createMemoryDriver(),
763
+ * tables: { workspaces: { id: stringShape(), snapshot: rawShape({}) } },
764
+ * })
765
+ * const store = new DatabaseWorkspaceStore(database.table('workspaces'))
755
766
  * ```
756
767
  */
757
768
  var DatabaseWorkspaceStore = class {
758
769
  #table;
759
770
  /**
760
- * Wrap a workspace snapshot table.
771
+ * Wraps a workspace snapshot table.
761
772
  *
762
773
  * @param table - The table keyed by workspace identifier
763
774
  */
@@ -782,7 +793,7 @@ var DatabaseWorkspaceStore = class {
782
793
  //#endregion
783
794
  //#region src/core/workspaces/stores/MemoryWorkspaceStore.ts
784
795
  /**
785
- * A process-local workspace snapshot store.
796
+ * Holds workspace snapshots in the current process.
786
797
  *
787
798
  * @example
788
799
  * ```ts
@@ -808,7 +819,7 @@ var MemoryWorkspaceStore = class {
808
819
  //#endregion
809
820
  //#region src/core/factories.ts
810
821
  /**
811
- * Create an immutable file with derived size and line counts.
822
+ * Creates an immutable file with derived size and line counts.
812
823
  *
813
824
  * @param input - The file path, content, and optional state
814
825
  * @returns A frozen file record
@@ -830,7 +841,7 @@ function createFile(input) {
830
841
  });
831
842
  }
832
843
  /**
833
- * Create the text arm of {@link FileContent}.
844
+ * Creates the text arm of {@link FileContent}.
834
845
  *
835
846
  * @param text - The text body
836
847
  * @param language - The language tag
@@ -850,9 +861,9 @@ function createTextContent(text, language) {
850
861
  };
851
862
  }
852
863
  /**
853
- * Create the binary arm of {@link FileContent}.
864
+ * Creates the binary arm of {@link FileContent}.
854
865
  *
855
- * @param data - The base64 payload
866
+ * @param base64 - The base64 payload
856
867
  * @param mime - The binary MIME
857
868
  * @returns Binary file content
858
869
  *
@@ -863,14 +874,14 @@ function createTextContent(text, language) {
863
874
  * createBinaryContent('AAAA', 'image/png')
864
875
  * ```
865
876
  */
866
- function createBinaryContent(data, mime) {
877
+ function createBinaryContent(base64, mime) {
867
878
  return {
868
- data,
879
+ base64,
869
880
  mime
870
881
  };
871
882
  }
872
883
  /**
873
- * Create a workspace.
884
+ * Creates a workspace.
874
885
  *
875
886
  * @param options - Optional identity, emitter configuration, and initial files
876
887
  * @returns A working workspace
@@ -887,7 +898,7 @@ function createWorkspace(options) {
887
898
  return new Workspace(options);
888
899
  }
889
900
  /**
890
- * Create an in-memory workspace snapshot store.
901
+ * Creates an in-memory workspace snapshot store.
891
902
  *
892
903
  * @returns A process-local workspace store
893
904
  *
@@ -902,9 +913,9 @@ function createMemoryWorkspaceStore() {
902
913
  return new MemoryWorkspaceStore();
903
914
  }
904
915
  /**
905
- * Create a database-backed workspace snapshot store.
916
+ * Creates a database-backed workspace snapshot store.
906
917
  *
907
- * @param driver - The database driver; defaults to an in-memory driver
918
+ * @param driver - The database driver. Default: an in-memory driver.
908
919
  * @returns A workspace store backed by the supplied driver
909
920
  *
910
921
  * @example
@@ -925,7 +936,7 @@ function createDatabaseWorkspaceStore(driver = (0, _orkestrel_database.createMem
925
936
  }).table("workspaces"));
926
937
  }
927
938
  /**
928
- * Create an empty workspace registry.
939
+ * Creates an empty workspace registry.
929
940
  *
930
941
  * @param options - Default event hooks and optional durability
931
942
  * @returns A workspace manager
@@ -950,6 +961,7 @@ exports.WorkspaceError = WorkspaceError;
950
961
  exports.WorkspaceManager = WorkspaceManager;
951
962
  exports.clampPosition = clampPosition;
952
963
  exports.clampRange = clampRange;
964
+ exports.computeDecodedSize = computeDecodedSize;
953
965
  exports.computeSize = computeSize;
954
966
  exports.countLines = countLines;
955
967
  exports.createBinaryContent = createBinaryContent;
@@ -959,7 +971,6 @@ exports.createMemoryWorkspaceStore = createMemoryWorkspaceStore;
959
971
  exports.createTextContent = createTextContent;
960
972
  exports.createWorkspace = createWorkspace;
961
973
  exports.createWorkspaceManager = createWorkspaceManager;
962
- exports.decodedSize = decodedSize;
963
974
  exports.escapeRegExp = escapeRegExp;
964
975
  exports.inferLanguage = inferLanguage;
965
976
  exports.isBinary = isBinary;
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs","names":["#id","#files","#emitter","#pattern","#write","#splice","#prepend","#append","#move","#remove","#workspaces","#on","#error","#store","#active","#drop","#table","#snapshots"],"sources":["../../../src/core/constants.ts","../../../src/core/errors.ts","../../../src/core/helpers.ts","../../../src/core/validators.ts","../../../src/core/workspaces/Workspace.ts","../../../src/core/workspaces/WorkspaceManager.ts","../../../src/core/workspaces/stores/DatabaseWorkspaceStore.ts","../../../src/core/workspaces/stores/MemoryWorkspaceStore.ts","../../../src/core/factories.ts"],"sourcesContent":["/**\n * Maps file extensions to language tags for text content.\n *\n * Unknown extensions intentionally fall back to `text` in\n * {@link import('./helpers.js').inferLanguage}.\n */\nexport const EXTENSION_LANGUAGES: Readonly<Record<string, string>> = Object.freeze({\n\tts: 'typescript',\n\ttsx: 'typescript',\n\tjs: 'javascript',\n\tjsx: 'javascript',\n\tmjs: 'javascript',\n\tcjs: 'javascript',\n\tjson: 'json',\n\tmd: 'markdown',\n\thtml: 'html',\n\thtm: 'html',\n\tcss: 'css',\n\tscss: 'scss',\n\tsass: 'sass',\n\tless: 'less',\n\tvue: 'vue',\n\tsvelte: 'svelte',\n\tpy: 'python',\n\trb: 'ruby',\n\trs: 'rust',\n\tgo: 'go',\n\tjava: 'java',\n\tkt: 'kotlin',\n\tswift: 'swift',\n\tc: 'c',\n\tcpp: 'cpp',\n\th: 'c',\n\thpp: 'cpp',\n\tcs: 'csharp',\n\tphp: 'php',\n\tsql: 'sql',\n\tsh: 'bash',\n\tbash: 'bash',\n\tzsh: 'bash',\n\tps1: 'powershell',\n\tyaml: 'yaml',\n\tyml: 'yaml',\n\ttoml: 'toml',\n\txml: 'xml',\n\tsvg: 'xml',\n\ttxt: 'text',\n})\n","import type { WorkspaceErrorCode } from './types.js'\n\n/** Reports an invalid workspace edit or search operation. */\nexport class WorkspaceError extends Error {\n\treadonly code: WorkspaceErrorCode\n\treadonly context?: Readonly<Record<string, unknown>>\n\n\t/**\n\t * Creates a workspace error.\n\t *\n\t * @param code - The machine-readable failure code\n\t * @param message - The human-readable failure message\n\t * @param context - Optional values associated with the failed operation\n\t */\n\tconstructor(\n\t\tcode: WorkspaceErrorCode,\n\t\tmessage: string,\n\t\tcontext?: Readonly<Record<string, unknown>>,\n\t) {\n\t\tsuper(message)\n\t\tthis.name = 'WorkspaceError'\n\t\tthis.code = code\n\t\tif (context !== undefined) this.context = context\n\t}\n}\n\n/**\n * Narrows a caught value to a {@link WorkspaceError}.\n *\n * @param value - The caught value\n * @returns True if the value is a workspace error; false otherwise\n *\n * @example\n * ```ts\n * if (isWorkspaceError(error)) error.code\n * ```\n */\nexport function isWorkspaceError(value: unknown): value is WorkspaceError {\n\treturn value instanceof WorkspaceError\n}\n","import type { BinaryContent, FileContent, Position, Range, TextContent } from './types.js'\nimport { EXTENSION_LANGUAGES } from './constants.js'\n\n/**\n * Infers a language tag from the final file extension.\n *\n * @param path - The file path\n * @returns The mapped language, or `text` when the extension is absent or unknown\n *\n * @example\n * ```ts\n * inferLanguage('src/main.ts') // 'typescript'\n * ```\n */\nexport function inferLanguage(path: string): string {\n\tconst dot = path.lastIndexOf('.')\n\tif (dot === -1) return 'text'\n\tconst extension = path.slice(dot + 1).toLowerCase()\n\treturn EXTENSION_LANGUAGES[extension] ?? 'text'\n}\n\n/**\n * Determines whether content is the text arm.\n *\n * @param content - The file content\n * @returns True if the content carries text and a language; false otherwise\n *\n * @example\n * ```ts\n * isText({ text: 'hello', language: 'text' }) // true\n * ```\n */\nexport function isText(content: FileContent): content is TextContent {\n\treturn 'text' in content\n}\n\n/**\n * Checks whether content is the binary arm.\n *\n * @param content - The file content\n * @returns True if the content carries a base64 string and a MIME; false otherwise\n *\n * @example\n * ```ts\n * isBinary({ base64: 'AAAA', mime: 'image/png' }) // true\n * ```\n */\nexport function isBinary(content: FileContent): content is BinaryContent {\n\treturn 'base64' in content\n}\n\n/**\n * Computes the byte size of file content.\n *\n * @param content - The content to measure\n * @returns UTF-8 bytes for text or decoded bytes for binary content\n *\n * @example\n * ```ts\n * computeSize({ text: 'café', language: 'text' }) // 5\n * ```\n */\nexport function computeSize(content: FileContent): number {\n\tif (isText(content)) return new TextEncoder().encode(content.text).length\n\treturn computeDecodedSize(content.base64)\n}\n\n/**\n * Counts the lines in file content.\n *\n * @param content - The content to inspect\n * @returns The text line count, or zero for empty text and binary content\n *\n * @example\n * ```ts\n * countLines({ text: 'a\\nb', language: 'text' }) // 2\n * ```\n */\nexport function countLines(content: FileContent): number {\n\tif (!isText(content)) return 0\n\tif (content.text.length === 0) return 0\n\tlet count = 1\n\tfor (const character of content.text) if (character === '\\n') count += 1\n\treturn count\n}\n\n/**\n * Computes the decoded byte length of a base64 string.\n *\n * @param base64 - The base64 string\n * @returns The decoded byte count\n *\n * @example\n * ```ts\n * computeDecodedSize('AAAA') // 3\n * ```\n */\nexport function computeDecodedSize(base64: string): number {\n\tlet padding = 0\n\tif (base64.endsWith('==')) padding = 2\n\telse if (base64.endsWith('=')) padding = 1\n\treturn Math.floor((base64.length * 3) / 4) - padding\n}\n\n/**\n * Determines whether a 1-based range is structurally valid.\n *\n * @param range - The range to inspect\n * @returns True if both positions are positive and ordered; false otherwise\n *\n * @example\n * ```ts\n * isValidRange({ start: { line: 1, column: 1 }, end: { line: 1, column: 2 } }) // true\n * ```\n */\nexport function isValidRange(range: Range): boolean {\n\tif (range.start.line < 1 || range.start.column < 1) return false\n\tif (range.end.line < 1 || range.end.column < 1) return false\n\tif (range.start.line > range.end.line) return false\n\treturn !(range.start.line === range.end.line && range.start.column > range.end.column)\n}\n\n/**\n * Clamps a position to text bounds.\n *\n * @param text - The addressed text\n * @param position - The requested position\n * @returns The clamped position\n *\n * @example\n * ```ts\n * clampPosition('ab', { line: 9, column: 9 }) // { line: 1, column: 3 }\n * ```\n */\nexport function clampPosition(text: string, position: Position): Position {\n\tconst lines = text.split('\\n')\n\tconst line = Math.max(1, Math.min(position.line, lines.length))\n\tconst lineText = lines[line - 1] ?? ''\n\tconst column = Math.max(1, Math.min(position.column, lineText.length + 1))\n\treturn { line, column }\n}\n\n/**\n * Clamps both positions in a range to text bounds.\n *\n * @param text - The addressed text\n * @param range - The requested range\n * @returns The clamped range\n *\n * @example\n * ```ts\n * clampRange('ab', { start: { line: 1, column: 1 }, end: { line: 9, column: 9 } })\n * ```\n */\nexport function clampRange(text: string, range: Range): Range {\n\treturn { start: clampPosition(text, range.start), end: clampPosition(text, range.end) }\n}\n\n/**\n * Converts a 1-based position to a zero-based string offset.\n *\n * @param text - The addressed text\n * @param position - The requested position\n * @returns The bounded string offset\n *\n * @example\n * ```ts\n * offsetAt('ab\\ncd', { line: 2, column: 1 }) // 3\n * ```\n */\nexport function offsetAt(text: string, position: Position): number {\n\tconst lines = text.split('\\n')\n\tlet offset = 0\n\tfor (let index = 0; index < position.line - 1 && index < lines.length; index += 1) {\n\t\toffset += (lines[index]?.length ?? 0) + 1\n\t}\n\toffset += position.column - 1\n\treturn Math.min(offset, text.length)\n}\n\n/**\n * Slices a clamped half-open text range.\n *\n * @param text - The source text\n * @param range - The requested range\n * @returns The ranged substring\n *\n * @example\n * ```ts\n * sliceRange('hello', { start: { line: 1, column: 1 }, end: { line: 1, column: 6 } }) // 'hello'\n * ```\n */\nexport function sliceRange(text: string, range: Range): string {\n\tconst clamped = clampRange(text, range)\n\treturn text.slice(offsetAt(text, clamped.start), offsetAt(text, clamped.end))\n}\n\n/**\n * Replaces a clamped half-open text range.\n *\n * @param text - The source text\n * @param range - The requested range\n * @param replacement - The replacement text\n * @returns The spliced text\n *\n * @example\n * ```ts\n * spliceRange('hello', { start: { line: 1, column: 1 }, end: { line: 1, column: 6 } }, 'bye')\n * ```\n */\nexport function spliceRange(text: string, range: Range, replacement: string): string {\n\tconst clamped = clampRange(text, range)\n\tconst start = offsetAt(text, clamped.start)\n\tconst end = offsetAt(text, clamped.end)\n\treturn text.slice(0, start) + replacement + text.slice(end)\n}\n\n/**\n * Assembles a nested range from four 1-based coordinates.\n *\n * @param fromLine - The start line\n * @param fromColumn - The start column\n * @param toLine - The end line\n * @param toColumn - The end column\n * @returns The assembled range without validation\n *\n * @example\n * ```ts\n * rangeOf(1, 1, 1, 2)\n * ```\n */\nexport function rangeOf(\n\tfromLine: number,\n\tfromColumn: number,\n\ttoLine: number,\n\ttoColumn: number,\n): Range {\n\treturn { start: { line: fromLine, column: fromColumn }, end: { line: toLine, column: toColumn } }\n}\n\n/**\n * Escapes regular-expression metacharacters for literal matching.\n *\n * @param value - The literal text\n * @returns The escaped regular-expression source\n *\n * @example\n * ```ts\n * escapeRegExp('a.b') // 'a\\\\.b'\n * ```\n */\nexport function escapeRegExp(value: string): string {\n\treturn value.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')\n}\n","import type { FileInterface, WorkspaceSnapshot } from './types.js'\nimport { arrayOf, holds, isNumber, isRecord, isString, literalOf } from '@orkestrel/contract'\n\n/**\n * Narrows an unknown value to an immutable file record.\n *\n * @param value - The value to inspect\n * @returns True if the value has the file shape; false otherwise\n *\n * @example\n * ```ts\n * isFile({ path: 'a.txt', content: { text: 'a', language: 'text' }, state: 'created', size: 1, lines: 1 })\n * ```\n */\nexport function isFile(value: unknown): value is FileInterface {\n\treturn holds(() => {\n\t\tif (!isRecord(value)) return false\n\t\tif (!isString(value.path) || !literalOf('created', 'modified')(value.state)) return false\n\t\tif (!isNumber(value.size) || !isNumber(value.lines)) return false\n\t\tif (!isRecord(value.content)) return false\n\t\tconst text = isString(value.content.text) && isString(value.content.language)\n\t\tconst binary =\n\t\t\tisString(value.content.base64) &&\n\t\t\tliteralOf('image/png', 'image/jpeg', 'image/gif', 'image/webp')(value.content.mime)\n\t\treturn text || binary\n\t})\n}\n\n/**\n * Narrows an unknown value to a workspace snapshot.\n *\n * @param value - The value read from a persistence boundary\n * @returns True if the value has the workspace snapshot shape; false otherwise\n *\n * @example\n * ```ts\n * isWorkspaceSnapshot({ id: 'work', files: [] }) // true\n * ```\n */\nexport function isWorkspaceSnapshot(value: unknown): value is WorkspaceSnapshot {\n\treturn holds(() => isRecord(value) && isString(value.id) && arrayOf(isFile)(value.files))\n}\n","import type { EmitterInterface } from '@orkestrel/emitter'\nimport type {\n\tFileInterface,\n\tRange,\n\tReadResult,\n\tReplaceResult,\n\tSearchMatch,\n\tSearchOptions,\n\tWorkspaceEventMap,\n\tWorkspaceInterface,\n\tWorkspaceOptions,\n\tWorkspaceSnapshot,\n} from '../types.js'\nimport { isArray, isRecord } from '@orkestrel/contract'\nimport { Emitter } from '@orkestrel/emitter'\nimport { WorkspaceError } from '../errors.js'\nimport { createFile } from '../factories.js'\nimport {\n\tclampRange,\n\tescapeRegExp,\n\tinferLanguage,\n\tisText,\n\tisValidRange,\n\tsliceRange,\n\tspliceRange,\n} from '../helpers.js'\n\n/**\n * Provides a mutable path-keyed editing surface over immutable files.\n *\n * Whole-file edits create text files, ranged edits operate only on existing text files, and\n * binary files remain available through construction-time hydration. Mutations emit after the\n * registry has changed.\n *\n * @example\n * ```ts\n * import { Workspace } from '@orkestrel/workspace'\n *\n * const workspace = new Workspace()\n * workspace.write('notes.txt', 'hello')\n * workspace.append('notes.txt', ' world')\n * workspace.read('notes.txt') // 'hello world'\n * ```\n */\nexport class Workspace implements WorkspaceInterface {\n\treadonly #id: string\n\treadonly #files = new Map<string, FileInterface>()\n\treadonly #emitter: Emitter<WorkspaceEventMap>\n\n\t/**\n\t * Creates a workspace.\n\t *\n\t * @param options - Optional identity, emitter configuration, and initial files\n\t */\n\tconstructor(options?: WorkspaceOptions) {\n\t\tthis.#id = options?.id ?? crypto.randomUUID()\n\t\tthis.#emitter = new Emitter<WorkspaceEventMap>({\n\t\t\t...(options?.on === undefined ? {} : { on: options.on }),\n\t\t\t...(options?.error === undefined ? {} : { error: options.error }),\n\t\t})\n\t\tif (options?.seed) for (const file of options.seed) this.#files.set(file.path, file)\n\t}\n\n\tget id(): string {\n\t\treturn this.#id\n\t}\n\n\tget emitter(): EmitterInterface<WorkspaceEventMap> {\n\t\treturn this.#emitter\n\t}\n\n\tget count(): number {\n\t\treturn this.#files.size\n\t}\n\n\tfile(path: string): FileInterface | undefined {\n\t\treturn this.#files.get(path)\n\t}\n\n\tfiles(): readonly FileInterface[] {\n\t\treturn [...this.#files.values()]\n\t}\n\n\tread(path: string): string | undefined\n\tread(path: string, range: Range): ReadResult | undefined\n\tread(paths: readonly string[]): Readonly<Record<string, string>>\n\tread(\n\t\tpath: string | readonly string[],\n\t\trange?: Range,\n\t): string | ReadResult | undefined | Readonly<Record<string, string>> {\n\t\tif (isArray(path)) {\n\t\t\tconst result: Record<string, string> = {}\n\t\t\tfor (const one of path) {\n\t\t\t\tconst file = this.#files.get(one)\n\t\t\t\tif (file && isText(file.content)) result[one] = file.content.text\n\t\t\t}\n\t\t\treturn result\n\t\t}\n\t\tconst file = this.#files.get(path)\n\t\tif (!file) return undefined\n\t\tif (!range) return isText(file.content) ? file.content.text : undefined\n\t\tif (!isText(file.content)) {\n\t\t\tthrow new WorkspaceError('MODALITY', `Cannot read a range of a binary file: ${path}`, {\n\t\t\t\tpath,\n\t\t\t})\n\t\t}\n\t\treturn {\n\t\t\tcontent: sliceRange(file.content.text, range),\n\t\t\trange: clampRange(file.content.text, range),\n\t\t}\n\t}\n\n\thas(path: string): boolean\n\thas(paths: readonly string[]): boolean\n\thas(path: string | readonly string[]): boolean {\n\t\tif (isArray(path)) return path.every((one) => this.#files.has(one))\n\t\treturn this.#files.has(path)\n\t}\n\n\tsearch(query: string, options?: SearchOptions): readonly SearchMatch[] {\n\t\tconst pattern = this.#pattern(query, options)\n\t\tconst limit = options?.limit\n\t\tconst matches: SearchMatch[] = []\n\t\tfor (const file of this.#files.values()) {\n\t\t\tif (limit !== undefined && matches.length >= limit) break\n\t\t\tif (!isText(file.content)) continue\n\t\t\tconst lines = file.content.text.split(/\\r\\n|\\n/)\n\t\t\tfor (let index = 0; index < lines.length; index += 1) {\n\t\t\t\tif (limit !== undefined && matches.length >= limit) break\n\t\t\t\tconst lineText = lines[index] ?? ''\n\t\t\t\tpattern.lastIndex = 0\n\t\t\t\tlet hit = pattern.exec(lineText)\n\t\t\t\twhile (hit !== null) {\n\t\t\t\t\tif (limit !== undefined && matches.length >= limit) break\n\t\t\t\t\tmatches.push({\n\t\t\t\t\t\tpath: file.path,\n\t\t\t\t\t\tline: index + 1,\n\t\t\t\t\t\tcolumn: hit.index + 1,\n\t\t\t\t\t\tlength: hit[0].length,\n\t\t\t\t\t\tcontent: lineText,\n\t\t\t\t\t})\n\t\t\t\t\tif (hit[0].length === 0) pattern.lastIndex += 1\n\t\t\t\t\thit = pattern.exec(lineText)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn matches\n\t}\n\n\treplace(query: string, replacement: string, options?: SearchOptions): ReplaceResult {\n\t\tconst pattern = this.#pattern(query, options)\n\t\tconst limit = options?.limit\n\t\tlet occurrences = 0\n\t\tlet files = 0\n\t\tfor (const [path, file] of this.#files) {\n\t\t\tif (limit !== undefined && occurrences >= limit) break\n\t\t\tif (!isText(file.content)) continue\n\t\t\tconst remaining = limit === undefined ? undefined : limit - occurrences\n\t\t\tlet count = 0\n\t\t\tpattern.lastIndex = 0\n\t\t\tconst next = file.content.text.replace(pattern, (match) => {\n\t\t\t\tif (remaining !== undefined && count >= remaining) return match\n\t\t\t\tcount += 1\n\t\t\t\treturn replacement\n\t\t\t})\n\t\t\tif (count > 0) {\n\t\t\t\toccurrences += count\n\t\t\t\tfiles += 1\n\t\t\t\tthis.write(path, next)\n\t\t\t}\n\t\t}\n\t\treturn { occurrences, files }\n\t}\n\n\twrite(path: string, content: string): void\n\twrite(path: string, content: string, range: Range): void\n\twrite(files: Readonly<Record<string, string>>): void\n\twrite(path: string | Readonly<Record<string, string>>, content?: string, range?: Range): void {\n\t\tif (isRecord(path)) {\n\t\t\tfor (const [one, text] of Object.entries(path)) this.#write(one, text)\n\t\t\treturn\n\t\t}\n\t\tif (content === undefined) return\n\t\tif (!range) {\n\t\t\tthis.#write(path, content)\n\t\t\treturn\n\t\t}\n\t\tthis.#splice(path, content, range)\n\t}\n\n\tprepend(path: string, content: string): void\n\tprepend(files: Readonly<Record<string, string>>): void\n\tprepend(path: string | Readonly<Record<string, string>>, content?: string): void {\n\t\tif (isRecord(path)) {\n\t\t\tfor (const [one, text] of Object.entries(path)) this.#prepend(one, text)\n\t\t\treturn\n\t\t}\n\t\tif (content === undefined) return\n\t\tthis.#prepend(path, content)\n\t}\n\n\tappend(path: string, content: string): void\n\tappend(files: Readonly<Record<string, string>>): void\n\tappend(path: string | Readonly<Record<string, string>>, content?: string): void {\n\t\tif (isRecord(path)) {\n\t\t\tfor (const [one, text] of Object.entries(path)) this.#append(one, text)\n\t\t\treturn\n\t\t}\n\t\tif (content === undefined) return\n\t\tthis.#append(path, content)\n\t}\n\n\tmove(from: string, to: string): boolean\n\tmove(mapping: Readonly<Record<string, string>>): boolean\n\tmove(from: string | Readonly<Record<string, string>>, to?: string): boolean {\n\t\tif (isRecord(from)) {\n\t\t\tlet moved = true\n\t\t\tfor (const [one, target] of Object.entries(from)) {\n\t\t\t\tif (!this.#move(one, target)) moved = false\n\t\t\t}\n\t\t\treturn moved\n\t\t}\n\t\tif (to === undefined) return false\n\t\treturn this.#move(from, to)\n\t}\n\n\tremove(path: string): boolean\n\tremove(paths: readonly string[]): boolean\n\tremove(path: string | readonly string[]): boolean {\n\t\tif (isArray(path)) {\n\t\t\tlet removed = true\n\t\t\tfor (const one of path) {\n\t\t\t\tif (!this.#remove(one)) removed = false\n\t\t\t}\n\t\t\treturn removed\n\t\t}\n\t\treturn this.#remove(path)\n\t}\n\n\tclear(): void {\n\t\tthis.#files.clear()\n\t\tthis.#emitter.emit('clear')\n\t}\n\n\tsnapshot(): WorkspaceSnapshot {\n\t\treturn { id: this.#id, files: this.files() }\n\t}\n\n\tdestroy(): void {\n\t\tthis.#emitter.destroy()\n\t}\n\n\t#write(path: string, content: string): void {\n\t\tconst existing = this.#files.get(path)\n\t\tconst language =\n\t\t\texisting && isText(existing.content) ? existing.content.language : inferLanguage(path)\n\t\tconst file = createFile({\n\t\t\tpath,\n\t\t\tcontent: { text: content, language },\n\t\t\tstate: existing ? 'modified' : 'created',\n\t\t})\n\t\tthis.#files.set(path, file)\n\t\tthis.#emitter.emit('write', file)\n\t}\n\n\t#splice(path: string, content: string, range: Range): void {\n\t\tconst existing = this.#files.get(path)\n\t\tif (!existing) {\n\t\t\tthrow new WorkspaceError('MISSING', `Cannot splice a range of a missing file: ${path}`, {\n\t\t\t\tpath,\n\t\t\t})\n\t\t}\n\t\tif (!isText(existing.content)) {\n\t\t\tthrow new WorkspaceError('MODALITY', `Cannot splice a range of a binary file: ${path}`, {\n\t\t\t\tpath,\n\t\t\t})\n\t\t}\n\t\tif (!isValidRange(range)) {\n\t\t\tthrow new WorkspaceError('RANGE', `Invalid range for file: ${path}`, { path, range })\n\t\t}\n\t\tconst file = createFile({\n\t\t\tpath,\n\t\t\tcontent: {\n\t\t\t\ttext: spliceRange(existing.content.text, range, content),\n\t\t\t\tlanguage: existing.content.language,\n\t\t\t},\n\t\t\tstate: 'modified',\n\t\t})\n\t\tthis.#files.set(path, file)\n\t\tthis.#emitter.emit('write', file)\n\t}\n\n\t#prepend(path: string, content: string): void {\n\t\tconst existing = this.#files.get(path)\n\t\tlet text = ''\n\t\tif (existing) {\n\t\t\tif (!isText(existing.content)) {\n\t\t\t\tthrow new WorkspaceError('MODALITY', `Cannot prepend text to a binary file: ${path}`, {\n\t\t\t\t\tpath,\n\t\t\t\t})\n\t\t\t}\n\t\t\ttext = existing.content.text\n\t\t}\n\t\tthis.#write(path, content + text)\n\t}\n\n\t#append(path: string, content: string): void {\n\t\tconst existing = this.#files.get(path)\n\t\tlet text = ''\n\t\tif (existing) {\n\t\t\tif (!isText(existing.content)) {\n\t\t\t\tthrow new WorkspaceError('MODALITY', `Cannot append text to a binary file: ${path}`, {\n\t\t\t\t\tpath,\n\t\t\t\t})\n\t\t\t}\n\t\t\ttext = existing.content.text\n\t\t}\n\t\tthis.#write(path, text + content)\n\t}\n\n\t#move(from: string, to: string): boolean {\n\t\tif (from === to) return false\n\t\tconst file = this.#files.get(from)\n\t\tif (!file) return false\n\t\tconst moved = createFile({ path: to, content: file.content, state: 'modified' })\n\t\tconst entries = [...this.#files]\n\t\tthis.#files.clear()\n\t\tfor (const [path, current] of entries) {\n\t\t\tif (path === from) this.#files.set(to, moved)\n\t\t\telse if (path !== to) this.#files.set(path, current)\n\t\t}\n\t\tthis.#emitter.emit('move', from, to)\n\t\treturn true\n\t}\n\n\t#remove(path: string): boolean {\n\t\tconst removed = this.#files.delete(path)\n\t\tif (removed) this.#emitter.emit('remove', path)\n\t\treturn removed\n\t}\n\n\t#pattern(query: string, options?: SearchOptions): RegExp {\n\t\tconst source = options?.regex === true ? query : escapeRegExp(query)\n\t\tconst flags = options?.sensitive === false ? 'gi' : 'g'\n\t\ttry {\n\t\t\treturn new RegExp(source, flags)\n\t\t} catch {\n\t\t\tthrow new WorkspaceError('PATTERN', `Invalid search pattern: ${query}`, { query })\n\t\t}\n\t}\n}\n","import type { EmitterErrorHandler, EmitterHooks } from '@orkestrel/emitter'\nimport type {\n\tWorkspaceEventMap,\n\tWorkspaceInterface,\n\tWorkspaceManagerInterface,\n\tWorkspaceManagerOptions,\n\tWorkspaceOptions,\n\tWorkspaceStoreInterface,\n} from '../types.js'\nimport { isArray } from '@orkestrel/contract'\nimport { Workspace } from './Workspace.js'\n\n/**\n * Provides an insertion-ordered workspace registry with an active selection.\n *\n * A supplied store adds lenient snapshot `open` and `save` operations. Event defaults flow into\n * workspaces created through the registry, while observability remains owned by each workspace.\n *\n * @example\n * ```ts\n * import { WorkspaceManager } from '@orkestrel/workspace'\n *\n * const manager = new WorkspaceManager()\n * const workspace = manager.add({ id: 'scratch' })\n * manager.active === workspace\n * ```\n */\nexport class WorkspaceManager implements WorkspaceManagerInterface {\n\treadonly #workspaces = new Map<string, WorkspaceInterface>()\n\t#active: string | undefined\n\treadonly #on: EmitterHooks<WorkspaceEventMap> | undefined\n\treadonly #error: EmitterErrorHandler | undefined\n\treadonly #store: WorkspaceStoreInterface | undefined\n\n\t/**\n\t * Creates a registry.\n\t *\n\t * @param options - Default event hooks and optional snapshot store\n\t */\n\tconstructor(options?: WorkspaceManagerOptions) {\n\t\tthis.#on = options?.on\n\t\tthis.#error = options?.error\n\t\tthis.#store = options?.store\n\t}\n\n\tget count(): number {\n\t\treturn this.#workspaces.size\n\t}\n\n\tget active(): WorkspaceInterface | undefined {\n\t\treturn this.#active === undefined ? undefined : this.#workspaces.get(this.#active)\n\t}\n\n\tworkspace(id: string): WorkspaceInterface | undefined {\n\t\treturn this.#workspaces.get(id)\n\t}\n\n\tworkspaces(): readonly WorkspaceInterface[] {\n\t\treturn [...this.#workspaces.values()]\n\t}\n\n\tadd(options?: WorkspaceOptions): WorkspaceInterface {\n\t\tconst on = options?.on ?? this.#on\n\t\tconst error = options?.error ?? this.#error\n\t\tconst workspace = new Workspace({\n\t\t\t...(options?.id === undefined ? {} : { id: options.id }),\n\t\t\t...(on === undefined ? {} : { on }),\n\t\t\t...(error === undefined ? {} : { error }),\n\t\t\t...(options?.seed === undefined ? {} : { seed: options.seed }),\n\t\t})\n\t\tthis.#workspaces.set(workspace.id, workspace)\n\t\tif (this.#active === undefined) this.#active = workspace.id\n\t\treturn workspace\n\t}\n\n\tswitch(id: string): WorkspaceInterface | undefined {\n\t\tconst workspace = this.#workspaces.get(id)\n\t\tif (workspace === undefined) return undefined\n\t\tthis.#active = id\n\t\treturn workspace\n\t}\n\n\tasync open(id: string): Promise<WorkspaceInterface | undefined> {\n\t\tconst existing = this.#workspaces.get(id)\n\t\tif (existing !== undefined) {\n\t\t\tthis.#active = id\n\t\t\treturn existing\n\t\t}\n\t\tif (this.#store === undefined) return undefined\n\t\tconst snapshot = await this.#store.get(id)\n\t\tif (snapshot === undefined) return undefined\n\t\tconst workspace = this.add({ id, seed: snapshot.files })\n\t\tthis.#active = workspace.id\n\t\treturn workspace\n\t}\n\n\tasync save(id: string): Promise<boolean> {\n\t\tconst workspace = this.#workspaces.get(id)\n\t\tif (this.#store === undefined || workspace === undefined) return false\n\t\tawait this.#store.set(workspace.snapshot())\n\t\treturn true\n\t}\n\n\tremove(id: string): boolean\n\tremove(ids: readonly string[]): boolean\n\tremove(ids: string | readonly string[]): boolean {\n\t\tif (isArray(ids)) {\n\t\t\tlet removed = true\n\t\t\tfor (const id of ids) {\n\t\t\t\tif (!this.#drop(id)) removed = false\n\t\t\t}\n\t\t\treturn removed\n\t\t}\n\t\treturn this.#drop(ids)\n\t}\n\n\tclear(): void {\n\t\tfor (const workspace of this.#workspaces.values()) workspace.destroy()\n\t\tthis.#workspaces.clear()\n\t\tthis.#active = undefined\n\t}\n\n\t#drop(id: string): boolean {\n\t\tconst workspace = this.#workspaces.get(id)\n\t\tif (workspace === undefined) return false\n\t\tthis.#workspaces.delete(id)\n\t\tworkspace.destroy()\n\t\tif (this.#active === id) this.#active = undefined\n\t\treturn true\n\t}\n}\n","import type {\n\tWorkspaceSnapshot,\n\tWorkspaceSnapshotRow,\n\tWorkspaceStoreInterface,\n} from '../../types.js'\nimport type { TableInterface } from '@orkestrel/database'\nimport { isWorkspaceSnapshot } from '../../validators.js'\n\n/**\n * Persists workspace snapshots in a database table.\n *\n * Snapshots occupy one opaque column and are narrowed when read back from the storage boundary.\n *\n * @example\n * ```ts\n * import { rawShape, stringShape } from '@orkestrel/contract'\n * import { createDatabase, createMemoryDriver } from '@orkestrel/database'\n * import { DatabaseWorkspaceStore } from '@orkestrel/workspace'\n *\n * const database = createDatabase({\n * \tdriver: createMemoryDriver(),\n * \ttables: { workspaces: { id: stringShape(), snapshot: rawShape({}) } },\n * })\n * const store = new DatabaseWorkspaceStore(database.table('workspaces'))\n * ```\n */\nexport class DatabaseWorkspaceStore implements WorkspaceStoreInterface {\n\treadonly #table: TableInterface<WorkspaceSnapshotRow>\n\n\t/**\n\t * Wraps a workspace snapshot table.\n\t *\n\t * @param table - The table keyed by workspace identifier\n\t */\n\tconstructor(table: TableInterface<WorkspaceSnapshotRow>) {\n\t\tthis.#table = table\n\t}\n\n\tasync get(id: string): Promise<WorkspaceSnapshot | undefined> {\n\t\tconst row = await this.#table.get(id)\n\t\tif (row === undefined) return undefined\n\t\treturn isWorkspaceSnapshot(row.snapshot) ? row.snapshot : undefined\n\t}\n\n\tasync set(snapshot: WorkspaceSnapshot): Promise<void> {\n\t\tawait this.#table.set({ id: snapshot.id, snapshot })\n\t}\n\n\tasync delete(id: string): Promise<void> {\n\t\tawait this.#table.remove(id)\n\t}\n}\n","import type { WorkspaceSnapshot, WorkspaceStoreInterface } from '../../types.js'\n\n/**\n * Holds workspace snapshots in the current process.\n *\n * @example\n * ```ts\n * import { MemoryWorkspaceStore } from '@orkestrel/workspace'\n *\n * const store = new MemoryWorkspaceStore()\n * ```\n */\nexport class MemoryWorkspaceStore implements WorkspaceStoreInterface {\n\treadonly #snapshots = new Map<string, WorkspaceSnapshot>()\n\n\tget(id: string): Promise<WorkspaceSnapshot | undefined> {\n\t\treturn Promise.resolve(this.#snapshots.get(id))\n\t}\n\n\tset(snapshot: WorkspaceSnapshot): Promise<void> {\n\t\tthis.#snapshots.set(snapshot.id, snapshot)\n\t\treturn Promise.resolve()\n\t}\n\n\tdelete(id: string): Promise<void> {\n\t\tthis.#snapshots.delete(id)\n\t\treturn Promise.resolve()\n\t}\n}\n","import type {\n\tBinaryContent,\n\tBinaryMIME,\n\tFileInput,\n\tFileInterface,\n\tTextContent,\n\tWorkspaceInterface,\n\tWorkspaceManagerInterface,\n\tWorkspaceManagerOptions,\n\tWorkspaceOptions,\n\tWorkspaceSnapshotRow,\n\tWorkspaceStoreInterface,\n} from './types.js'\nimport type { DriverInterface, TableInterface } from '@orkestrel/database'\nimport { rawShape, stringShape } from '@orkestrel/contract'\nimport { createDatabase, createMemoryDriver } from '@orkestrel/database'\nimport { computeSize, countLines } from './helpers.js'\nimport { Workspace } from './workspaces/Workspace.js'\nimport { WorkspaceManager } from './workspaces/WorkspaceManager.js'\nimport { DatabaseWorkspaceStore } from './workspaces/stores/DatabaseWorkspaceStore.js'\nimport { MemoryWorkspaceStore } from './workspaces/stores/MemoryWorkspaceStore.js'\n\n/**\n * Creates an immutable file with derived size and line counts.\n *\n * @param input - The file path, content, and optional state\n * @returns A frozen file record\n *\n * @example\n * ```ts\n * import { createFile, createTextContent } from '@orkestrel/workspace'\n *\n * createFile({ path: 'a.txt', content: createTextContent('hello', 'text') })\n * ```\n */\nexport function createFile(input: FileInput): FileInterface {\n\treturn Object.freeze({\n\t\tpath: input.path,\n\t\tcontent: input.content,\n\t\tstate: input.state ?? 'created',\n\t\tsize: computeSize(input.content),\n\t\tlines: countLines(input.content),\n\t})\n}\n\n/**\n * Creates the text arm of {@link FileContent}.\n *\n * @param text - The text body\n * @param language - The language tag\n * @returns Text file content\n *\n * @example\n * ```ts\n * import { createTextContent } from '@orkestrel/workspace'\n *\n * createTextContent('hello', 'text')\n * ```\n */\nexport function createTextContent(text: string, language: string): TextContent {\n\treturn { text, language }\n}\n\n/**\n * Creates the binary arm of {@link FileContent}.\n *\n * @param base64 - The base64 payload\n * @param mime - The binary MIME\n * @returns Binary file content\n *\n * @example\n * ```ts\n * import { createBinaryContent } from '@orkestrel/workspace'\n *\n * createBinaryContent('AAAA', 'image/png')\n * ```\n */\nexport function createBinaryContent(base64: string, mime: BinaryMIME): BinaryContent {\n\treturn { base64, mime }\n}\n\n/**\n * Creates a workspace.\n *\n * @param options - Optional identity, emitter configuration, and initial files\n * @returns A working workspace\n *\n * @example\n * ```ts\n * import { createWorkspace } from '@orkestrel/workspace'\n *\n * const workspace = createWorkspace()\n * workspace.write('a.txt', 'hello')\n * ```\n */\nexport function createWorkspace(options?: WorkspaceOptions): WorkspaceInterface {\n\treturn new Workspace(options)\n}\n\n/**\n * Creates an in-memory workspace snapshot store.\n *\n * @returns A process-local workspace store\n *\n * @example\n * ```ts\n * import { createMemoryWorkspaceStore } from '@orkestrel/workspace'\n *\n * const store = createMemoryWorkspaceStore()\n * ```\n */\nexport function createMemoryWorkspaceStore(): WorkspaceStoreInterface {\n\treturn new MemoryWorkspaceStore()\n}\n\n/**\n * Creates a database-backed workspace snapshot store.\n *\n * @param driver - The database driver. Default: an in-memory driver.\n * @returns A workspace store backed by the supplied driver\n *\n * @example\n * ```ts\n * import { createDatabaseWorkspaceStore } from '@orkestrel/workspace'\n *\n * const store = createDatabaseWorkspaceStore()\n * ```\n */\nexport function createDatabaseWorkspaceStore(\n\tdriver: DriverInterface = createMemoryDriver(),\n): WorkspaceStoreInterface {\n\tconst columns = { id: stringShape(), snapshot: rawShape({}) }\n\tconst database = createDatabase({ driver, tables: { workspaces: columns } })\n\tconst table: TableInterface<WorkspaceSnapshotRow> = database.table('workspaces')\n\treturn new DatabaseWorkspaceStore(table)\n}\n\n/**\n * Creates an empty workspace registry.\n *\n * @param options - Default event hooks and optional durability\n * @returns A workspace manager\n *\n * @example\n * ```ts\n * import { createWorkspaceManager } from '@orkestrel/workspace'\n *\n * const manager = createWorkspaceManager()\n * manager.add()\n * ```\n */\nexport function createWorkspaceManager(\n\toptions?: WorkspaceManagerOptions,\n): WorkspaceManagerInterface {\n\treturn new WorkspaceManager(options)\n}\n"],"mappings":"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"}