ac-storage 0.14.1 → 0.15.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.
package/dist/bundle.cjs CHANGED
@@ -2,8 +2,8 @@
2
2
 
3
3
  var fs = require('node:fs');
4
4
  var fs$1 = require('node:fs/promises');
5
- var fs$2 = require('fs');
6
5
  var path = require('node:path');
6
+ var fs$2 = require('fs');
7
7
 
8
8
  function _interopNamespaceDefault(e) {
9
9
  var n = Object.create(null);
@@ -24,8 +24,8 @@ function _interopNamespaceDefault(e) {
24
24
 
25
25
  var fs__namespace$2 = /*#__PURE__*/_interopNamespaceDefault(fs);
26
26
  var fs__namespace = /*#__PURE__*/_interopNamespaceDefault(fs$1);
27
- var fs__namespace$1 = /*#__PURE__*/_interopNamespaceDefault(fs$2);
28
27
  var path__namespace = /*#__PURE__*/_interopNamespaceDefault(path);
28
+ var fs__namespace$1 = /*#__PURE__*/_interopNamespaceDefault(fs$2);
29
29
 
30
30
  let e$1 = class e extends Error{constructor(e){super(e),this.name="TreeNavigateError";}};const t$1="--tree-leaf";var i$1;let r$1 = class r{#e={};#t=".";#i=false;#r=true;constructor(){}static from(e,t={}){const r=new i$1;return r.#e=e,r.#t=t.delimiter??".",r.#i=t.allowWildcard??false,r.#r=t.allowRecursiveWildcard??true,r}subtree(t){const r=t.split(this.#t),a=this.#a(r,this.#e);if(!a)throw new e$1(`Path '${t}' does not exist`);if(i$1.#l(a.value)||i$1.#s(a.value))throw new e$1(`Path '${t}' is not a subtree`);const l=a.path.reduce((e,t)=>e[t],this.#e),s=new i$1;return s.#e=l,s.#t=this.#t,s.#i=this.#i,s}get(e,t){return this.walk(e,t)?.value??null}walk(e,t={}){if(""===e)return t.allowIntermediate?{value:this.#e,path:[]}:null;const r=e.split(this.#t),a=this.#a(r,this.#e);if(!a)return null;const l=a.value;return i$1.#l(l)?a:i$1.#s(l)?{value:l.value,path:a.path}:t.allowIntermediate?a:null}trace(e){const t=e.split(this.#t),r={treePath:[]},a=this.#a(t,this.#e,r);let l,s;if(a){const e=a.value;return i$1.#l(e)?(l=true,s=e):i$1.#s(e)?(l=true,s=e.value):(l=false,s=e),{find:true,isLeaf:l,value:s,nodePath:a?.path??[],tracePath:r.treePath,untracePath:t}}return {find:false,isLeaf:false,value:void 0,nodePath:[],tracePath:r.treePath,untracePath:t}}#a(e,t,r={treePath:[]}){const{treePath:a}=r,l=e.shift();if(null==l)return {value:t,path:[]};if("object"!=typeof t||i$1.#s(t))return e.unshift(l),null;if(a.push(l),l in t){const i=this.#a(e,t[l],r);if(i)return {value:i.value,path:[l,...i.path]}}else if(this.#i){if("*"in t){const i=this.#a(e,t["*"],r);if(i)return {value:i.value,path:["*",...i.path]}}if(this.#r&&"**/*"in t)return {value:t["**/*"],path:["**/*"]}}return null}static#l(e){return null==e||"object"!=typeof e}static#s(e){return 1==e[t$1]}};i$1=r$1;
31
31
 
@@ -513,31 +513,73 @@ class SchemaFlattener {
513
513
  }
514
514
 
515
515
  class JSONFS {
516
+ #writeLock = Promise.resolve();
516
517
  async read(filename) {
517
- let contents;
518
- if (fs.existsSync(filename)) {
519
- const jsonText = await fs__namespace.readFile(filename, 'utf8');
520
- try {
521
- contents = JSON.parse(jsonText);
522
- }
523
- catch {
524
- contents = {};
525
- }
518
+ if (!fs.existsSync(filename)) {
519
+ return {};
526
520
  }
527
- else {
528
- contents = {};
521
+ const jsonText = await fs__namespace.readFile(filename, 'utf8');
522
+ if (jsonText.trim() === '') {
523
+ throw new Error(`JSON file is empty: "${filename}"`);
524
+ }
525
+ try {
526
+ return JSON.parse(jsonText);
527
+ }
528
+ catch (error) {
529
+ throw new Error(`Failed to parse JSON from "${filename}": ` +
530
+ `${error instanceof Error ? error.message : String(error)}. ` +
531
+ `Content preview: "${jsonText.substring(0, 100)}..."`);
529
532
  }
530
- return contents;
531
533
  }
532
534
  async write(filename, contents) {
535
+ // Serialize writes using a lock chain
536
+ const previousLock = this.#writeLock;
537
+ let releaseLock;
538
+ this.#writeLock = new Promise((resolve) => {
539
+ releaseLock = resolve;
540
+ });
541
+ try {
542
+ // Wait for previous write to complete
543
+ await previousLock;
544
+ // Perform atomic write
545
+ await this.#atomicWrite(filename, contents);
546
+ }
547
+ finally {
548
+ releaseLock();
549
+ }
550
+ }
551
+ async #atomicWrite(filename, contents) {
533
552
  const jsonString = JSON.stringify(contents, null, 4);
534
- await fs__namespace.writeFile(filename, jsonString, 'utf8');
553
+ const dir = path__namespace.dirname(filename);
554
+ // Generate unique temp file name
555
+ const tempFile = `${filename}.tmp.${Date.now()}.${Math.random().toString(36).substring(2)}`;
556
+ try {
557
+ // Ensure parent directory exists (create if needed)
558
+ await fs__namespace.mkdir(dir, { recursive: true });
559
+ // Write to temp file
560
+ await fs__namespace.writeFile(tempFile, jsonString, 'utf8');
561
+ // Atomic rename (this is atomic on POSIX systems)
562
+ await fs__namespace.rename(tempFile, filename);
563
+ }
564
+ catch (error) {
565
+ // Clean up temp file on failure
566
+ try {
567
+ if (fs.existsSync(tempFile)) {
568
+ await fs__namespace.rm(tempFile);
569
+ }
570
+ }
571
+ catch {
572
+ // Ignore cleanup errors
573
+ }
574
+ throw error;
575
+ }
535
576
  }
536
577
  async rm(filename) {
537
578
  try {
538
579
  await fs__namespace.rm(filename);
539
580
  }
540
- catch (error) {
581
+ catch {
582
+ // Ignore errors (file might not exist)
541
583
  }
542
584
  }
543
585
  async exists(filename) {
@@ -565,36 +607,28 @@ class DefaultValueProvider {
565
607
  constructor(navigate = null) {
566
608
  this.#navigate = navigate;
567
609
  }
568
- /**
569
- *
570
- */
571
- get(key, baseValue) {
610
+ get(key) {
572
611
  if (this.#navigate == null) {
573
- return baseValue;
612
+ return undefined;
574
613
  }
575
614
  const result = this.#navigate.walk(key, { allowIntermediate: true });
576
615
  if (result == null) {
577
616
  return undefined;
578
617
  }
579
618
  else if (isJSONTypeData(result.value)) {
580
- if (baseValue != null) {
581
- return baseValue;
582
- }
583
- else {
584
- const defaultValue = result.value.default_value;
585
- return ((defaultValue != null)
586
- ? defaultValue
587
- : undefined);
588
- }
619
+ const defaultValue = result.value.default_value;
620
+ return ((defaultValue != null)
621
+ ? defaultValue
622
+ : undefined);
589
623
  }
590
624
  else {
591
625
  // for (const [key, value] of Object.entries(result.value)) {
592
626
  // console.log(key, value);
593
627
  // }
594
- return this.#getDefaultData(result.value, baseValue);
628
+ return this.#getDefaultData(result.value);
595
629
  }
596
630
  }
597
- #getDefaultData(typeData, baseValue) {
631
+ #getDefaultData(typeData) {
598
632
  if (t$1 in typeData) {
599
633
  if (typeData.value.default_value != null) {
600
634
  return typeData.value.default_value;
@@ -602,17 +636,18 @@ class DefaultValueProvider {
602
636
  return undefined;
603
637
  }
604
638
  else {
605
- let result = baseValue;
639
+ let hasKey = false;
640
+ const result = {};
606
641
  for (const [key, value] of Object.entries(typeData)) {
607
642
  const defaultValue = this.#getDefaultData(value);
608
643
  if (defaultValue !== undefined) {
609
- result ??= {};
610
- if (!(key in result)) {
611
- result[key] = defaultValue;
612
- }
644
+ result[key] = defaultValue;
645
+ hasKey = true;
613
646
  }
614
647
  }
615
- return result;
648
+ return (hasKey
649
+ ? result
650
+ : undefined);
616
651
  }
617
652
  }
618
653
  }
@@ -692,8 +727,8 @@ class JSONAccessor {
692
727
  getOne(key) {
693
728
  this.#ensureNotDropped();
694
729
  let value = this.#getData(key);
695
- if (value == null || typeof value === 'object') {
696
- value = this.#defaultValueProvider.get(key, value);
730
+ if (value == null) {
731
+ value = this.#defaultValueProvider.get(key);
697
732
  }
698
733
  return value;
699
734
  }
@@ -702,8 +737,8 @@ class JSONAccessor {
702
737
  const result = {};
703
738
  for (const key of keys) {
704
739
  let value = this.#getData(key);
705
- if (value == null || typeof value === 'object') {
706
- value = this.#defaultValueProvider.get(key, value);
740
+ if (value == null) {
741
+ value = this.#defaultValueProvider.get(key);
707
742
  }
708
743
  const resolved = resolveNestedRef(result, key, true);
709
744
  resolved.parent[resolved.key] = value;
@@ -889,7 +924,7 @@ class BinaryAccessor {
889
924
  async writeBase64(data) {
890
925
  this.#ensureNotDropped();
891
926
  const buffer = Buffer.from(data, 'base64');
892
- this.write(buffer);
927
+ await this.write(buffer);
893
928
  }
894
929
  async readBase64() {
895
930
  this.#ensureNotDropped();
@@ -897,9 +932,11 @@ class BinaryAccessor {
897
932
  return buffer.toString('base64');
898
933
  }
899
934
  async drop() {
900
- this.#ensureNotDropped();
935
+ if (this.#dropped)
936
+ return;
937
+ this.#dropped = true;
901
938
  if (fs.existsSync(this.#filePath)) {
902
- fs__namespace.rm(this.#filePath, { force: true });
939
+ await fs__namespace.rm(this.#filePath, { force: true });
903
940
  }
904
941
  }
905
942
  async commit() {
@@ -1486,7 +1523,7 @@ class StorageAccessControl {
1486
1523
  chainDependency();
1487
1524
  return ac;
1488
1525
  }
1489
- async release(identifier) {
1526
+ async destroy(identifier) {
1490
1527
  const walked = this.accessTree.walk(identifier);
1491
1528
  if (!walked) {
1492
1529
  throw new NotRegisterError(`'${identifier}' is not registered.`);
@@ -1494,11 +1531,11 @@ class StorageAccessControl {
1494
1531
  if (this.checkAccessIsDirectory(walked.value)) {
1495
1532
  throw new DirectoryAccessError(`> '${identifier}' is directory.`);
1496
1533
  }
1497
- await this.#events.onRelease(identifier);
1534
+ await this.#events.onDestroy(identifier);
1498
1535
  }
1499
- async releaseDir(identifier) {
1536
+ async destroyDir(identifier) {
1500
1537
  this.validateDirectoryPath(identifier);
1501
- await this.#events.onRelease(identifier);
1538
+ await this.#events.onDestroy(identifier);
1502
1539
  }
1503
1540
  getAccessType(identifier) {
1504
1541
  const getAT = (access) => {
@@ -1522,6 +1559,16 @@ class StorageAccessControl {
1522
1559
  }
1523
1560
  }
1524
1561
  }
1562
+ validateAccess(identifier, accessType) {
1563
+ const walked = this.accessTree.walk(identifier, { allowIntermediate: true });
1564
+ if (!walked) {
1565
+ throw new NotRegisterError(`'${identifier}' is not registered.`);
1566
+ }
1567
+ if (this.checkAccessIsDirectory(walked.value)) {
1568
+ throw new DirectoryAccessError(`'${identifier}' is directory.`);
1569
+ }
1570
+ return this.validateAndResolveAccess(walked.value, accessType, identifier);
1571
+ }
1525
1572
  validateDirectoryPath(identifier) {
1526
1573
  const walked = this.accessTree.walk(identifier, { allowIntermediate: true });
1527
1574
  if (!walked || !this.checkAccessIsDirectory(walked.value)) {
@@ -1597,6 +1644,30 @@ class ACSubStorage {
1597
1644
  async accessAsBinary(identifier) {
1598
1645
  return await this.access(identifier, 'binary');
1599
1646
  }
1647
+ async create(identifier, accessType) {
1648
+ return await this.#master.create(this.#prefix + ':' + identifier, accessType);
1649
+ }
1650
+ async createAsJSON(identifier) {
1651
+ return await this.create(identifier, 'json');
1652
+ }
1653
+ async createAsText(identifier) {
1654
+ return await this.create(identifier, 'text');
1655
+ }
1656
+ async createAsBinary(identifier) {
1657
+ return await this.create(identifier, 'binary');
1658
+ }
1659
+ async open(identifier, accessType) {
1660
+ return await this.#master.open(this.#prefix + ':' + identifier, accessType);
1661
+ }
1662
+ async openAsJSON(identifier) {
1663
+ return await this.open(identifier, 'json');
1664
+ }
1665
+ async openAsText(identifier) {
1666
+ return await this.open(identifier, 'text');
1667
+ }
1668
+ async openAsBinary(identifier) {
1669
+ return await this.open(identifier, 'binary');
1670
+ }
1600
1671
  async copy(oldIdentifier, newIdentifier) {
1601
1672
  await this.#master.copy(this.#prefix + ':' + oldIdentifier, this.#prefix + ':' + newIdentifier);
1602
1673
  }
@@ -1612,6 +1683,15 @@ class ACSubStorage {
1612
1683
  async dropAll() {
1613
1684
  await this.#master.dropDir(this.#prefix);
1614
1685
  }
1686
+ async release(identifier) {
1687
+ await this.#master.release(this.#prefix + ':' + identifier);
1688
+ }
1689
+ async releaseDir(identifier) {
1690
+ await this.#master.releaseDir(this.#prefix + ':' + identifier);
1691
+ }
1692
+ async releaseAll() {
1693
+ await this.#master.releaseDir(this.#prefix);
1694
+ }
1615
1695
  async commit(identifier = '') {
1616
1696
  await this.#master.commit(this.#prefix + ':' + identifier);
1617
1697
  }
@@ -1652,63 +1732,87 @@ class ACStorage {
1652
1732
  const cacheData = JSON.stringify(this.accessCache, null, 4);
1653
1733
  fs__namespace$2.writeFileSync(this.cachePath, cacheData, 'utf8');
1654
1734
  }
1655
- initAccessControl() {
1656
- const onAccess = async (identifier, sa) => {
1657
- const targetPath = path__namespace.join(this.basePath, identifier.replaceAll(':', '/'));
1658
- this.eventListeners.access?.(identifier, sa);
1659
- let item = this.accessors.get(identifier);
1660
- if (item != undefined && !item.isDropped()) {
1661
- return item;
1735
+ async getOrCreateAccessorFromAccess(identifier, sa, mode) {
1736
+ const targetPath = path__namespace.join(this.basePath, identifier.replaceAll(':', '/'));
1737
+ this.eventListeners.access?.(identifier, sa);
1738
+ let item = this.accessors.get(identifier);
1739
+ if (item != undefined && !item.isDropped()) {
1740
+ if (mode === 'create') {
1741
+ throw new StorageError(`File '${identifier}' already exists in memory`);
1662
1742
  }
1663
- let acm;
1664
- switch (sa.accessType) {
1665
- case 'directory':
1666
- acm = DirectoryAccessorManager.fromFS(targetPath, sa.tree);
1667
- break;
1668
- case 'json':
1669
- acm = JSONAccessorManager.fromFS(targetPath, sa.structure);
1670
- break;
1671
- case 'binary':
1672
- acm = BinaryAccessorManager.fromFS(targetPath);
1673
- break;
1674
- case 'text':
1675
- acm = TextAccessorManager.fromFS(targetPath);
1676
- break;
1677
- case 'custom':
1678
- const event = this.customAccessEvents[sa.id];
1679
- if (!event) {
1680
- throw new StorageError('Invalid access type');
1681
- }
1682
- const ac = await event.init(targetPath, ...sa.args);
1683
- acm = CustomAccessorManager.from(ac, {
1684
- customId: sa.id,
1685
- event,
1686
- actualPath: targetPath,
1687
- customArgs: sa.args,
1688
- });
1689
- break;
1690
- default:
1691
- // 기본 타입 이외에는 custom 타입으로 wrap되기 때문에 이 경우가 발생하지 않음
1743
+ return item;
1744
+ }
1745
+ let acm;
1746
+ switch (sa.accessType) {
1747
+ case 'directory':
1748
+ acm = DirectoryAccessorManager.fromFS(targetPath, sa.tree);
1749
+ break;
1750
+ case 'json':
1751
+ acm = JSONAccessorManager.fromFS(targetPath, sa.structure);
1752
+ break;
1753
+ case 'binary':
1754
+ acm = BinaryAccessorManager.fromFS(targetPath);
1755
+ break;
1756
+ case 'text':
1757
+ acm = TextAccessorManager.fromFS(targetPath);
1758
+ break;
1759
+ case 'custom':
1760
+ const event = this.customAccessEvents[sa.id];
1761
+ if (!event) {
1692
1762
  throw new StorageError('Invalid access type');
1763
+ }
1764
+ const ac = await event.init(targetPath, ...sa.args);
1765
+ acm = CustomAccessorManager.from(ac, {
1766
+ customId: sa.id,
1767
+ event,
1768
+ actualPath: targetPath,
1769
+ customArgs: sa.args,
1770
+ });
1771
+ break;
1772
+ default:
1773
+ throw new StorageError('Invalid access type');
1774
+ }
1775
+ const exists = await acm.exists();
1776
+ if (mode === 'create') {
1777
+ if (exists) {
1778
+ throw new StorageError(`File '${identifier}' already exists on disk`);
1693
1779
  }
1694
- if (!await acm.exists())
1780
+ await acm.create();
1781
+ }
1782
+ else if (mode === 'open') {
1783
+ if (!exists) {
1784
+ throw new StorageError(`File '${identifier}' does not exist`);
1785
+ }
1786
+ await acm.load();
1787
+ }
1788
+ else {
1789
+ if (!exists)
1695
1790
  await acm.create();
1696
1791
  else
1697
1792
  await acm.load();
1698
- this.accessCache[identifier] = sa.accessType !== 'custom' ? sa.accessType : sa.id;
1699
- this.accessors.set(identifier, acm);
1700
- return acm;
1793
+ }
1794
+ this.accessCache[identifier] = sa.accessType !== 'custom' ? sa.accessType : sa.id;
1795
+ this.accessors.set(identifier, acm);
1796
+ return acm;
1797
+ }
1798
+ async getOrCreateAccessor(identifier, accessType, mode) {
1799
+ const sa = this.accessControl.validateAccess(identifier, accessType);
1800
+ return await this.getOrCreateAccessorFromAccess(identifier, sa, mode);
1801
+ }
1802
+ initAccessControl() {
1803
+ const onAccess = async (identifier, sa) => {
1804
+ return await this.getOrCreateAccessorFromAccess(identifier, sa, 'access');
1701
1805
  };
1702
- const onRelease = async (identifier) => {
1806
+ const onDestroy = async (identifier) => {
1703
1807
  const accessor = this.accessors.get(identifier);
1704
1808
  if (!accessor)
1705
1809
  return;
1706
1810
  for (const child of accessor.dependent) {
1707
- await onRelease(child);
1811
+ await onDestroy(child);
1708
1812
  }
1709
1813
  if (identifier === '')
1710
1814
  return;
1711
- this.eventListeners.release?.(identifier);
1815
+ this.eventListeners.destroy?.(identifier);
1712
1816
  if (!accessor.isDropped())
1713
1817
  await accessor.drop();
1714
1818
  delete this.accessCache[identifier];
@@ -1722,7 +1826,7 @@ class ACStorage {
1722
1826
  };
1723
1827
  return new StorageAccessControl({
1724
1828
  onAccess,
1725
- onRelease,
1829
+ onDestroy,
1726
1830
  onChainDependency,
1727
1831
  });
1728
1832
  }
@@ -1731,8 +1835,8 @@ class ACStorage {
1731
1835
  case 'access':
1732
1836
  this.eventListeners.access = listener;
1733
1837
  break;
1734
- case 'release':
1735
- this.eventListeners.release = listener;
1838
+ case 'destroy':
1839
+ this.eventListeners.destroy = listener;
1736
1840
  break;
1737
1841
  }
1738
1842
  }
@@ -1761,6 +1865,32 @@ class ACStorage {
1761
1865
  async accessAsBinary(identifier) {
1762
1866
  return await this.access(identifier, 'binary');
1763
1867
  }
1868
+ async create(identifier, accessType) {
1869
+ const acm = await this.getOrCreateAccessor(identifier, accessType, 'create');
1870
+ return acm.accessor;
1871
+ }
1872
+ async createAsJSON(identifier) {
1873
+ return await this.create(identifier, 'json');
1874
+ }
1875
+ async createAsText(identifier) {
1876
+ return await this.create(identifier, 'text');
1877
+ }
1878
+ async createAsBinary(identifier) {
1879
+ return await this.create(identifier, 'binary');
1880
+ }
1881
+ async open(identifier, accessType) {
1882
+ const acm = await this.getOrCreateAccessor(identifier, accessType, 'open');
1883
+ return acm.accessor;
1884
+ }
1885
+ async openAsJSON(identifier) {
1886
+ return await this.open(identifier, 'json');
1887
+ }
1888
+ async openAsText(identifier) {
1889
+ return await this.open(identifier, 'text');
1890
+ }
1891
+ async openAsBinary(identifier) {
1892
+ return await this.open(identifier, 'binary');
1893
+ }
1764
1894
  async copy(oldIdentifier, newIdentifier) {
1765
1895
  const accessType = this.validateAndGetAccessTypePair(oldIdentifier, newIdentifier);
1766
1896
  await this.commit(oldIdentifier);
@@ -1808,13 +1938,40 @@ class ACStorage {
1808
1938
  if (identifier === '') {
1809
1939
  throw new StorageError('Cannot drop the root directory. use dropAll() instead.');
1810
1940
  }
1811
- await this.accessControl.releaseDir(identifier);
1941
+ await this.accessControl.destroyDir(identifier);
1812
1942
  }
1813
1943
  async drop(identifier) {
1814
- await this.accessControl.release(identifier);
1944
+ await this.accessControl.destroy(identifier);
1815
1945
  }
1816
1946
  async dropAll() {
1817
- await this.accessControl.releaseDir('');
1947
+ await this.accessControl.destroyDir('');
1948
+ }
1949
+ async release(identifier) {
1950
+ await this.commit(identifier);
1951
+ await this.#unloadFromMemory(identifier);
1952
+ }
1953
+ async releaseDir(identifier) {
1954
+ if (identifier === '') {
1955
+ throw new StorageError('Cannot release the root directory. use releaseAll() instead.');
1956
+ }
1957
+ await this.commit(identifier);
1958
+ await this.#unloadFromMemory(identifier);
1959
+ }
1960
+ async releaseAll() {
1961
+ await this.commitAll();
1962
+ await this.#unloadFromMemory('');
1963
+ }
1964
+ async #unloadFromMemory(identifier) {
1965
+ const accessor = this.accessors.get(identifier);
1966
+ if (!accessor)
1967
+ return;
1968
+ for (const child of accessor.dependent) {
1969
+ await this.#unloadFromMemory(child);
1970
+ }
1971
+ if (identifier !== '') {
1972
+ delete this.accessCache[identifier];
1973
+ this.accessors.delete(identifier);
1974
+ }
1818
1975
  }
1819
1976
  async commit(identifier = '') {
1820
1977
  await this.#commitRecursive(identifier);
@@ -1840,62 +1997,82 @@ class MemACStorage extends ACStorage {
1840
1997
  constructor() {
1841
1998
  super('', { noCache: true });
1842
1999
  }
1843
- initAccessControl() {
1844
- const onAccess = async (identifier, sa) => {
1845
- this.eventListeners.access?.(identifier, sa);
1846
- let item = this.accessors.get(identifier);
1847
- if (item != undefined && !item.isDropped()) {
1848
- return item;
2000
+ async getOrCreateAccessorFromAccess(identifier, sa, mode) {
2001
+ this.eventListeners.access?.(identifier, sa);
2002
+ let item = this.accessors.get(identifier);
2003
+ if (item != undefined && !item.isDropped()) {
2004
+ if (mode === 'create') {
2005
+ throw new StorageError(`File '${identifier}' already exists in memory`);
1849
2006
  }
1850
- let acm;
1851
- switch (sa.accessType) {
1852
- case 'directory':
1853
- acm = DirectoryAccessorManager.fromMemory(sa.tree);
1854
- break;
1855
- case 'json':
1856
- acm = JSONAccessorManager.fromMemory(sa.structure);
1857
- break;
1858
- case 'binary':
1859
- acm = BinaryAccessorManager.fromMemory();
1860
- break;
1861
- case 'text':
1862
- acm = TextAccessorManager.fromMemory();
1863
- break;
1864
- case 'custom':
1865
- const event = this.customAccessEvents[sa.id];
1866
- if (!event) {
1867
- throw new StorageError('Invalid access type');
1868
- }
1869
- const ac = await event.init(null, ...sa.args);
1870
- acm = CustomAccessorManager.from(ac, {
1871
- customId: sa.id,
1872
- event,
1873
- actualPath: null,
1874
- customArgs: sa.args,
1875
- });
1876
- break;
1877
- default:
1878
- // 기본 타입 이외에는 custom 타입으로 wrap되기 때문에 이 경우가 발생하지 않음
1879
- throw new StorageError('Logic Error : Invalid access type');
2007
+ return item;
2008
+ }
2009
+ let acm;
2010
+ switch (sa.accessType) {
2011
+ case 'directory':
2012
+ acm = DirectoryAccessorManager.fromMemory(sa.tree);
2013
+ break;
2014
+ case 'json':
2015
+ acm = JSONAccessorManager.fromMemory(sa.structure);
2016
+ break;
2017
+ case 'binary':
2018
+ acm = BinaryAccessorManager.fromMemory();
2019
+ break;
2020
+ case 'text':
2021
+ acm = TextAccessorManager.fromMemory();
2022
+ break;
2023
+ case 'custom':
2024
+ const event = this.customAccessEvents[sa.id];
2025
+ if (!event) {
2026
+ throw new StorageError('Invalid access type');
2027
+ }
2028
+ const ac = await event.init(null, ...sa.args);
2029
+ acm = CustomAccessorManager.from(ac, {
2030
+ customId: sa.id,
2031
+ event,
2032
+ actualPath: null,
2033
+ customArgs: sa.args,
2034
+ });
2035
+ break;
2036
+ default:
2037
+ throw new StorageError('Logic Error : Invalid access type');
2038
+ }
2039
+ const exists = await acm.exists();
2040
+ if (mode === 'create') {
2041
+ if (exists) {
2042
+ throw new StorageError(`File '${identifier}' already exists in memory`);
1880
2043
  }
1881
- if (!acm.exists())
1882
- acm.create();
2044
+ await acm.create();
2045
+ }
2046
+ else if (mode === 'open') {
2047
+ if (!exists) {
2048
+ throw new StorageError(`File '${identifier}' does not exist`);
2049
+ }
2050
+ await acm.load();
2051
+ }
2052
+ else {
2053
+ if (!exists)
2054
+ await acm.create();
1883
2055
  else
1884
- acm.load();
1885
- this.accessCache[identifier] = sa.accessType !== 'custom' ? sa.accessType : sa.id;
1886
- this.accessors.set(identifier, acm);
1887
- return acm;
2056
+ await acm.load();
2057
+ }
2058
+ this.accessCache[identifier] = sa.accessType !== 'custom' ? sa.accessType : sa.id;
2059
+ this.accessors.set(identifier, acm);
2060
+ return acm;
2061
+ }
2062
+ initAccessControl() {
2063
+ const onAccess = async (identifier, sa) => {
2064
+ return await this.getOrCreateAccessorFromAccess(identifier, sa, 'access');
1888
2065
  };
1889
- const onRelease = async (identifier) => {
2066
+ const onDestroy = async (identifier) => {
1890
2067
  const accessor = this.accessors.get(identifier);
1891
2068
  if (!accessor)
1892
2069
  return;
1893
2070
  for (const child of accessor.dependent) {
1894
- await onRelease(child);
2071
+ await onDestroy(child);
1895
2072
  }
1896
2073
  if (identifier === '')
1897
2074
  return;
1898
- this.eventListeners.release?.(identifier);
2075
+ this.eventListeners.destroy?.(identifier);
1899
2076
  if (!accessor.isDropped())
1900
2077
  accessor.drop();
1901
2078
  delete this.accessCache[identifier];
@@ -1909,10 +2086,37 @@ class MemACStorage extends ACStorage {
1909
2086
  };
1910
2087
  return new StorageAccessControl({
1911
2088
  onAccess,
1912
- onRelease,
2089
+ onDestroy,
1913
2090
  onChainDependency,
1914
2091
  });
1915
2092
  }
2093
+ async release(identifier) {
2094
+ await this.commit(identifier);
2095
+ await this.#unloadFromMemory(identifier);
2096
+ }
2097
+ async releaseDir(identifier) {
2098
+ if (identifier === '') {
2099
+ throw new StorageError('Cannot release the root directory. use releaseAll() instead.');
2100
+ }
2101
+ await this.commit(identifier);
2102
+ await this.#unloadFromMemory(identifier);
2103
+ }
2104
+ async releaseAll() {
2105
+ await this.commitAll();
2106
+ await this.#unloadFromMemory('');
2107
+ }
2108
+ async #unloadFromMemory(identifier) {
2109
+ const accessor = this.accessors.get(identifier);
2110
+ if (!accessor)
2111
+ return;
2112
+ for (const child of accessor.dependent) {
2113
+ await this.#unloadFromMemory(child);
2114
+ }
2115
+ if (identifier !== '') {
2116
+ delete this.accessCache[identifier];
2117
+ this.accessors.delete(identifier);
2118
+ }
2119
+ }
1916
2120
  }
1917
2121
 
1918
2122
  class StorageAccess {