@soulcraft/brainy 6.4.0 → 6.5.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.
@@ -285,8 +285,22 @@ export declare class VirtualFileSystem implements IVirtualFileSystem {
285
285
  type: string;
286
286
  metadata: any;
287
287
  }>>;
288
+ /**
289
+ * Sort bulk operations to prevent race conditions
290
+ *
291
+ * Strategy:
292
+ * 1. mkdir operations first, sorted by path depth (shallowest first)
293
+ * 2. Other operations (write, delete, update) after, in original order
294
+ *
295
+ * This ensures parent directories exist before files are written,
296
+ * preventing duplicate entity creation from concurrent mkdir calls.
297
+ */
298
+ private sortBulkOperations;
288
299
  /**
289
300
  * Bulk write operations for performance
301
+ *
302
+ * v6.5.0: Prevents race condition by processing mkdir operations
303
+ * sequentially before parallel batch processing of other operations.
290
304
  */
291
305
  bulkWrite(operations: Array<{
292
306
  type: 'write' | 'delete' | 'mkdir' | 'update';
@@ -1930,8 +1930,40 @@ export class VirtualFileSystem {
1930
1930
  metadata: result.entity?.metadata || {}
1931
1931
  }));
1932
1932
  }
1933
+ /**
1934
+ * Sort bulk operations to prevent race conditions
1935
+ *
1936
+ * Strategy:
1937
+ * 1. mkdir operations first, sorted by path depth (shallowest first)
1938
+ * 2. Other operations (write, delete, update) after, in original order
1939
+ *
1940
+ * This ensures parent directories exist before files are written,
1941
+ * preventing duplicate entity creation from concurrent mkdir calls.
1942
+ */
1943
+ sortBulkOperations(operations) {
1944
+ const mkdirOps = [];
1945
+ const otherOps = [];
1946
+ for (const op of operations) {
1947
+ if (op.type === 'mkdir') {
1948
+ mkdirOps.push(op);
1949
+ }
1950
+ else {
1951
+ otherOps.push(op);
1952
+ }
1953
+ }
1954
+ // Sort mkdir by path depth (shallowest first)
1955
+ mkdirOps.sort((a, b) => {
1956
+ const depthA = (a.path.match(/\//g) || []).length;
1957
+ const depthB = (b.path.match(/\//g) || []).length;
1958
+ return depthA !== depthB ? depthA - depthB : a.path.localeCompare(b.path);
1959
+ });
1960
+ return [...mkdirOps, ...otherOps];
1961
+ }
1933
1962
  /**
1934
1963
  * Bulk write operations for performance
1964
+ *
1965
+ * v6.5.0: Prevents race condition by processing mkdir operations
1966
+ * sequentially before parallel batch processing of other operations.
1935
1967
  */
1936
1968
  async bulkWrite(operations) {
1937
1969
  await this.ensureInitialized();
@@ -1939,10 +1971,31 @@ export class VirtualFileSystem {
1939
1971
  successful: 0,
1940
1972
  failed: []
1941
1973
  };
1942
- // Process operations in batches for better performance
1974
+ // Sort operations: mkdirs first (by depth), then others
1975
+ const sortedOps = this.sortBulkOperations(operations);
1976
+ // Separate mkdir operations for sequential processing
1977
+ const mkdirOps = sortedOps.filter(op => op.type === 'mkdir');
1978
+ const otherOps = sortedOps.filter(op => op.type !== 'mkdir');
1979
+ // Phase 1: Process mkdir operations SEQUENTIALLY
1980
+ // This prevents the race condition where parallel mkdir calls
1981
+ // create duplicate directory entities due to mutex timing window
1982
+ for (const op of mkdirOps) {
1983
+ try {
1984
+ await this.mkdir(op.path, op.options);
1985
+ result.successful++;
1986
+ }
1987
+ catch (error) {
1988
+ result.failed.push({
1989
+ operation: op,
1990
+ error: error.message || 'Unknown error'
1991
+ });
1992
+ }
1993
+ }
1994
+ // Phase 2: Process other operations in parallel batches
1995
+ // These can safely run in parallel since parent directories now exist
1943
1996
  const batchSize = 10;
1944
- for (let i = 0; i < operations.length; i += batchSize) {
1945
- const batch = operations.slice(i, i + batchSize);
1997
+ for (let i = 0; i < otherOps.length; i += batchSize) {
1998
+ const batch = otherOps.slice(i, i + batchSize);
1946
1999
  // Process batch in parallel
1947
2000
  const promises = batch.map(async (op) => {
1948
2001
  try {
@@ -1953,9 +2006,6 @@ export class VirtualFileSystem {
1953
2006
  case 'delete':
1954
2007
  await this.unlink(op.path);
1955
2008
  break;
1956
- case 'mkdir':
1957
- await this.mkdir(op.path, op.options);
1958
- break;
1959
2009
  case 'update': {
1960
2010
  // Update only metadata without changing content
1961
2011
  const entityId = await this.pathResolver.resolve(op.path);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@soulcraft/brainy",
3
- "version": "6.4.0",
3
+ "version": "6.5.0",
4
4
  "description": "Universal Knowledge Protocol™ - World's first Triple Intelligence database unifying vector, graph, and document search in one API. Stage 3 CANONICAL: 42 nouns × 127 verbs covering 96-97% of all human knowledge.",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.js",