@meistrari/vault-sdk 1.8.3 → 2.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.
package/dist/index.mjs CHANGED
@@ -231,7 +231,7 @@ function getFileName(content) {
231
231
  }
232
232
 
233
233
  const name = "@meistrari/vault-sdk";
234
- const version = "1.8.3";
234
+ const version = "2.0.0";
235
235
  const license = "UNLICENSED";
236
236
  const repository = {
237
237
  type: "git",
@@ -258,8 +258,8 @@ const scripts = {
258
258
  check: "bun run lint && bun tsc --noEmit"
259
259
  };
260
260
  const dependencies = {
261
- "@meistrari/vault-shared": "workspace:*",
262
261
  "@meistrari/file-type": "22.0.0",
262
+ "@meistrari/vault-shared": "workspace:*",
263
263
  "mime-types": "3.0.1",
264
264
  ofetch: "1.4.1",
265
265
  zod: "3.23.8"
@@ -320,6 +320,7 @@ async function wrappedFetch(url, requestInit) {
320
320
  };
321
321
  const request = new Request(url, options);
322
322
  request.headers.set("User-Agent", userAgent);
323
+ request.headers.set("x-compatibility-date", compatibilityDate);
323
324
  const response = await fetch(request);
324
325
  if (!response.ok) {
325
326
  throw await FetchError.from(request.url, request.method, response);
@@ -363,9 +364,7 @@ class VaultFile {
363
364
  * @returns The headers for the request
364
365
  */
365
366
  get headers() {
366
- const headers = this.config.authStrategy.getHeaders();
367
- headers.set("User-Agent", userAgent);
368
- return headers;
367
+ return this.config.authStrategy.getHeaders();
369
368
  }
370
369
  /**
371
370
  * Performs a request to the vault service and handles the response or errors.
@@ -382,7 +381,6 @@ class VaultFile {
382
381
  const { method, path, body, signal, query } = params;
383
382
  const url = new URL(path, this.baseUrl);
384
383
  const headers = new Headers(this.headers);
385
- headers.set("x-compatibility-date", compatibilityDate);
386
384
  if (query) {
387
385
  Object.entries(query).forEach(([key, value]) => {
388
386
  url.searchParams.set(key, value);
@@ -411,6 +409,7 @@ class VaultFile {
411
409
  * @param metadata.mimeType - The mime type of the file
412
410
  * @param metadata.parentId - The ID of the parent file for hierarchical file relationships
413
411
  * @param metadata.onMissingParent - What to do if the parent file does not exist or has been deleted
412
+ * @param metadata.onParentConflict - What to do if the file already exists as an active child of another file
414
413
  * @param options - The options for the request
415
414
  * @param options.signal - The signal to abort the request
416
415
  *
@@ -427,6 +426,7 @@ class VaultFile {
427
426
  mimeType: metadata.mimeType,
428
427
  parentId: metadata.parentId,
429
428
  onMissingParent: metadata.onMissingParent,
429
+ onParentConflict: metadata.onParentConflict,
430
430
  fileName: this.name,
431
431
  sha256sum: this.id ?? this.metadata?.id ?? (this.content ? await getFileHash(this.content) : void 0)
432
432
  }),
@@ -518,6 +518,7 @@ class VaultFile {
518
518
  * @param params.upload - Whether to upload the file (default: false)
519
519
  * @param params.parentId - The ID of the parent file for hierarchical file relationships
520
520
  * @param params.onMissingParent - What to do if the parent file does not exist or has been deleted
521
+ * @param params.onParentConflict - What to do if the file already exists as an active child of another file
521
522
  * @param options - The options for the request
522
523
  * @param options.signal - The signal to abort the request
523
524
  *
@@ -570,7 +571,7 @@ class VaultFile {
570
571
  * ```
571
572
  */
572
573
  static async fromContent(params, options) {
573
- const { content, config: vaultConfig, upload = false, parentId, onMissingParent } = params;
574
+ const { content, config: vaultConfig, upload = false, parentId, onMissingParent, onParentConflict } = params;
574
575
  const name = basename(params.name) ?? getFileName(content);
575
576
  const config = resolveConfig(vaultConfig);
576
577
  const { vaultUrl, authStrategy } = config;
@@ -590,7 +591,8 @@ class VaultFile {
590
591
  size,
591
592
  mimeType,
592
593
  parentId,
593
- onMissingParent
594
+ onMissingParent,
595
+ onParentConflict
594
596
  }, { signal: options?.signal });
595
597
  if (upload) {
596
598
  await file.upload(file.content, createdFile.uploadUrl, { signal: options?.signal });
@@ -608,6 +610,7 @@ class VaultFile {
608
610
  * @param params.contentType - The MIME type of the content (optional)
609
611
  * @param params.parentId - The ID of the parent file for hierarchical file relationships
610
612
  * @param params.onMissingParent - What to do if the parent file does not exist or has been deleted
613
+ * @param params.onParentConflict - What to do if the file already exists as an active child of another file
611
614
  * @param options - The options for the request
612
615
  * @param options.signal - The signal to abort the request
613
616
  *
@@ -644,7 +647,14 @@ class VaultFile {
644
647
  * ```
645
648
  */
646
649
  static async fromStream(params, options) {
647
- const { contentLength, config: vaultConfig, contentType, parentId, onMissingParent } = params;
650
+ const {
651
+ contentLength,
652
+ config: vaultConfig,
653
+ contentType,
654
+ parentId,
655
+ onMissingParent,
656
+ onParentConflict
657
+ } = params;
648
658
  const name = basename(params.name);
649
659
  const config = resolveConfig(vaultConfig);
650
660
  const file = new VaultFile({ config, name });
@@ -652,10 +662,89 @@ class VaultFile {
652
662
  size: contentLength,
653
663
  mimeType: contentType || "application/octet-stream",
654
664
  parentId,
655
- onMissingParent
665
+ onMissingParent,
666
+ onParentConflict
656
667
  }, { signal: options?.signal });
657
668
  return file;
658
669
  }
670
+ /**
671
+ * Creates multiple VaultFile instances from given content in a single request.
672
+ *
673
+ * @param params - Parameters for creating VaultFiles from content
674
+ * @param params.files - Array of file inputs with content and optional metadata
675
+ * @param params.config - The configuration for the VaultFiles
676
+ * @param params.upload - Whether to upload all files (default: false)
677
+ * @param options - The options for the request
678
+ * @param options.signal - The signal to abort the request
679
+ *
680
+ * @returns Array of new VaultFile instances
681
+ *
682
+ * @example
683
+ * ```ts
684
+ * const files = await VaultFile.fromContentBulk({
685
+ * files: [
686
+ * { content: blob1, name: 'file1.txt' },
687
+ * { content: blob2, name: 'file2.txt', parentId: 'parent-id' },
688
+ * ],
689
+ * config: { vaultUrl, authStrategy },
690
+ * upload: true
691
+ * })
692
+ * ```
693
+ */
694
+ static async fromContentBulk(params, options) {
695
+ const { files: fileInputs, config: vaultConfig, upload = false } = params;
696
+ const config = resolveConfig(vaultConfig);
697
+ const preparedFiles = await Promise.all(
698
+ fileInputs.map(async (input) => {
699
+ const name = basename(input.name) ?? getFileName(input.content);
700
+ const sha256sum = await getFileHash(input.content);
701
+ const mimeType = input.mimeType ?? await detectFileMimeType(input.content);
702
+ const size = input.content.size;
703
+ return { ...input, name, sha256sum, mimeType, size };
704
+ })
705
+ );
706
+ const url = new URL("files/bulk", config.vaultUrl);
707
+ const headers = config.authStrategy.getHeaders();
708
+ headers.set("Content-Type", "application/json");
709
+ const body = preparedFiles.map((file) => ({
710
+ fileName: file.name,
711
+ sha256sum: file.sha256sum,
712
+ mimeType: file.mimeType,
713
+ size: file.size,
714
+ parentId: file.parentId,
715
+ onMissingParent: file.onMissingParent,
716
+ onParentConflict: file.onParentConflict
717
+ }));
718
+ const response = await wrappedFetch(url, {
719
+ method: "POST",
720
+ headers,
721
+ body: JSON.stringify(body),
722
+ signal: options?.signal
723
+ });
724
+ const data = await response.json();
725
+ const vaultFiles = data.map((item, index) => {
726
+ const file = new VaultFile({
727
+ config,
728
+ id: item.id,
729
+ name: item.metadata?.originalFileName ?? preparedFiles[index].name,
730
+ content: preparedFiles[index].content,
731
+ metadata: item.metadata
732
+ });
733
+ file.lastUploadUrl = {
734
+ url: new URL(item.uploadUrl),
735
+ expiresAt: new Date(item.expiresAt)
736
+ };
737
+ return file;
738
+ });
739
+ if (upload) {
740
+ await Promise.all(
741
+ vaultFiles.map(
742
+ (file) => file.upload(void 0, void 0, { signal: options?.signal })
743
+ )
744
+ );
745
+ }
746
+ return vaultFiles;
747
+ }
659
748
  /**
660
749
  * Fetches and populates the metadata fields for this VaultFile instance.
661
750
  *
@@ -1577,8 +1666,80 @@ class VaultFile {
1577
1666
  options
1578
1667
  );
1579
1668
  }
1669
+ /**
1670
+ * Updates the metadata of this file in the vault.
1671
+ *
1672
+ * This method allows you to modify specific metadata properties of the file, such as renaming it.
1673
+ * The instance's metadata property is automatically updated with the response from the server.
1674
+ * Only the provided parameters will be updated; all other metadata remains unchanged.
1675
+ *
1676
+ * @param params - The metadata properties to update
1677
+ * @param params.name - The new name to assign to the file
1678
+ * @param options - Additional options for the request
1679
+ * @param options.signal - AbortSignal to cancel the request
1680
+ *
1681
+ * @returns A Promise that resolves when the update completes successfully
1682
+ * @throws {Error} If the file ID is not set
1683
+ * @throws {FetchError} If the update request fails
1684
+ *
1685
+ * @example
1686
+ * ```ts
1687
+ * // Rename a file
1688
+ * const vaultFile = await VaultFile.fromVaultReference({
1689
+ * reference: 'vault://file-id',
1690
+ * config: { vaultUrl, authStrategy }
1691
+ * })
1692
+ * await vaultFile.updateMetadata({ name: 'new-name.txt' })
1693
+ * console.log('File renamed to:', vaultFile.metadata?.originalFileName)
1694
+ * ```
1695
+ *
1696
+ * @example
1697
+ * ```ts
1698
+ * // Rename a file with abort signal
1699
+ * const controller = new AbortController()
1700
+ * await vaultFile.updateMetadata(
1701
+ * { name: 'document-v2.pdf' },
1702
+ * { signal: controller.signal }
1703
+ * )
1704
+ * // Later: controller.abort()
1705
+ * ```
1706
+ *
1707
+ * @example
1708
+ * ```ts
1709
+ * // Rename after upload
1710
+ * const file = new File(['content'], 'temp.txt')
1711
+ * const vaultFile = await VaultFile.fromContent({
1712
+ * name: 'temp.txt',
1713
+ * content: file,
1714
+ * config: { vaultUrl, authStrategy },
1715
+ * upload: true
1716
+ * })
1717
+ * // Later, rename the file
1718
+ * await vaultFile.updateMetadata({ name: 'final-document.txt' })
1719
+ * ```
1720
+ */
1721
+ async updateMetadata(params, options) {
1722
+ if (!this.id) {
1723
+ throw new Error("File ID is not set");
1724
+ }
1725
+ const updatedMetadata = await this._fetch({
1726
+ method: "PATCH",
1727
+ path: `files/${this.id}`,
1728
+ body: JSON.stringify(params),
1729
+ signal: options?.signal
1730
+ });
1731
+ this.metadata = updatedMetadata;
1732
+ }
1580
1733
  }
1581
1734
 
1735
+ const VAULT_REFERENCE_UUID_REGEX = /^vault:\/\/[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
1736
+ const VAULT_REFERENCE_SHA256_REGEX = /^vault:\/\/[a-f0-9]{64}$/i;
1737
+ function isValidUuidV4(uuid) {
1738
+ return /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(uuid);
1739
+ }
1740
+ function isValidSha256(sha256) {
1741
+ return /^[a-f0-9]{64}$/i.test(sha256);
1742
+ }
1582
1743
  function isS3UrlExpired(url) {
1583
1744
  try {
1584
1745
  const urlObj = new URL(url);
@@ -1602,59 +1763,94 @@ function isS3UrlExpired(url) {
1602
1763
  }
1603
1764
  }
1604
1765
  function isVaultReference(url) {
1605
- return url.startsWith("vault://") && url.length === 10;
1766
+ return VAULT_REFERENCE_UUID_REGEX.test(url) || VAULT_REFERENCE_SHA256_REGEX.test(url);
1606
1767
  }
1607
- function isVaultFileS3Url(url) {
1608
- const urlObj = new URL(url);
1609
- const amzDate = urlObj.searchParams.get("X-Amz-Date");
1610
- const amzExpires = urlObj.searchParams.get("X-Amz-Expires");
1611
- return urlObj.hostname.includes("amazonaws.com") && Boolean(amzDate) && Boolean(amzExpires);
1768
+ function isPresignedS3Url(url) {
1769
+ try {
1770
+ const urlObj = new URL(url);
1771
+ const amzDate = urlObj.searchParams.get("X-Amz-Date");
1772
+ const amzExpires = urlObj.searchParams.get("X-Amz-Expires");
1773
+ return urlObj.hostname.includes("amazonaws.com") && Boolean(amzDate) && Boolean(amzExpires);
1774
+ } catch {
1775
+ return false;
1776
+ }
1612
1777
  }
1613
1778
  const URL_STRATEGIES = [
1614
- {
1615
- separator: "/",
1616
- extractSegments: ([workspaceId, vaultFileId]) => ({
1617
- workspaceId,
1618
- vaultFileId
1619
- })
1620
- },
1621
1779
  {
1622
1780
  separator: "_",
1623
1781
  extractSegments: ([vaultFileId, workspaceId]) => ({
1624
1782
  vaultFileId,
1625
1783
  workspaceId
1626
1784
  })
1785
+ },
1786
+ {
1787
+ separator: "/",
1788
+ extractSegments: ([workspaceId, vaultFileId]) => ({
1789
+ workspaceId,
1790
+ vaultFileId
1791
+ })
1627
1792
  }
1628
1793
  ];
1629
- function extractVaultFileIdFromS3Url(url) {
1630
- if (isVaultReference(url))
1631
- return vaultUtils.file.getFileIdFromVaultReference(url);
1794
+ function extractVaultFileIdFromLegacyS3Url(url) {
1632
1795
  try {
1633
- if (!isVaultFileS3Url(url))
1634
- return null;
1635
1796
  const urlObj = new URL(url);
1636
1797
  const strategy = URL_STRATEGIES.find((strategy2) => urlObj.pathname.includes(strategy2.separator));
1637
1798
  if (!strategy)
1638
1799
  return null;
1639
- const segments = urlObj.pathname.split(strategy.separator).filter((segment) => segment.length > 0);
1800
+ const segments = urlObj.pathname.split(strategy.separator).map((segment) => segment.replace(/^\/+|\/+$/g, "")).filter((segment) => segment.length > 0);
1640
1801
  if (segments.length < 2)
1641
1802
  return null;
1642
1803
  const extractedIds = strategy.extractSegments(segments);
1643
- if (!extractedIds || !extractedIds.vaultFileId || !extractedIds.workspaceId || extractedIds.vaultFileId.length < 32)
1804
+ if (!extractedIds.vaultFileId || !extractedIds.workspaceId || extractedIds.vaultFileId.length < 32)
1644
1805
  return null;
1645
1806
  return extractedIds.vaultFileId;
1646
1807
  } catch {
1647
1808
  return null;
1648
1809
  }
1649
1810
  }
1811
+ function extractVaultFileIdFromS3Url(url) {
1812
+ if (isVaultReference(url))
1813
+ return vaultUtils.file.getFileIdFromVaultReference(url);
1814
+ if (!isPresignedS3Url(url))
1815
+ return null;
1816
+ if (isTaggedVaultPresignedUrl(url))
1817
+ return getVaultParamsFromS3Url(url)?.fileId ?? null;
1818
+ return extractVaultFileIdFromLegacyS3Url(url);
1819
+ }
1650
1820
  function convertS3UrlToVaultReference(url) {
1651
1821
  const vaultFileId = extractVaultFileIdFromS3Url(url);
1652
1822
  return vaultFileId ? `vault://${vaultFileId}` : null;
1653
1823
  }
1824
+ function isTaggedVaultPresignedUrl(url) {
1825
+ try {
1826
+ const urlObj = new URL(url);
1827
+ const workspaceId = urlObj.searchParams.get("vault-workspace-id");
1828
+ const fileId = urlObj.searchParams.get("vault-file-id");
1829
+ if (!workspaceId || !fileId) {
1830
+ return false;
1831
+ }
1832
+ const hasWorkspaceId = isValidUuidV4(workspaceId);
1833
+ const hasFileId = isValidSha256(fileId) || isValidUuidV4(fileId);
1834
+ const hasAmazonDomain = urlObj.hostname.endsWith("amazonaws.com");
1835
+ return hasWorkspaceId && hasFileId && hasAmazonDomain;
1836
+ } catch {
1837
+ return false;
1838
+ }
1839
+ }
1840
+ function getVaultParamsFromS3Url(url) {
1841
+ if (!isTaggedVaultPresignedUrl(url))
1842
+ return null;
1843
+ const urlObj = new URL(url);
1844
+ return {
1845
+ fileId: urlObj.searchParams.get("vault-file-id"),
1846
+ workspaceId: urlObj.searchParams.get("vault-workspace-id"),
1847
+ parentId: urlObj.searchParams.get("vault-parent-id")
1848
+ };
1849
+ }
1654
1850
 
1655
1851
  function vaultClient(vaultConfig) {
1656
1852
  const config = resolveConfig(vaultConfig);
1657
- function createFromContent(nameOrContent, contentOrNameOrOptions, options) {
1853
+ async function createFromContent(nameOrContent, contentOrNameOrOptions, options) {
1658
1854
  const isOptionsObject = (value) => {
1659
1855
  return typeof value === "object" && value !== null && !(value instanceof Blob) && !(value instanceof File);
1660
1856
  };
@@ -1665,7 +1861,7 @@ function vaultClient(vaultConfig) {
1665
1861
  const parentId2 = options?.parentId;
1666
1862
  const mimeType2 = options?.mimeType;
1667
1863
  const upload2 = options?.upload;
1668
- return VaultFile.fromContent({ content: content2, name: name2, config, parentId: parentId2, mimeType: mimeType2, upload: upload2 }, { signal: signal2 });
1864
+ return await VaultFile.fromContent({ content: content2, name: name2, config, parentId: parentId2, mimeType: mimeType2, upload: upload2 }, { signal: signal2 });
1669
1865
  }
1670
1866
  const content = nameOrContent;
1671
1867
  const name = typeof contentOrNameOrOptions === "string" ? contentOrNameOrOptions : void 0;
@@ -1673,16 +1869,16 @@ function vaultClient(vaultConfig) {
1673
1869
  const parentId = isOptionsObject(contentOrNameOrOptions) ? contentOrNameOrOptions.parentId : options?.parentId;
1674
1870
  const mimeType = isOptionsObject(contentOrNameOrOptions) ? contentOrNameOrOptions.mimeType : options?.mimeType;
1675
1871
  const upload = isOptionsObject(contentOrNameOrOptions) ? contentOrNameOrOptions.upload : options?.upload;
1676
- return VaultFile.fromContent({ content, name, config, parentId, mimeType, upload }, { signal });
1872
+ return await VaultFile.fromContent({ content, name, config, parentId, mimeType, upload }, { signal });
1677
1873
  }
1678
- function createFromReference(reference, options) {
1679
- return VaultFile.fromVaultReference({
1874
+ async function createFromReference(reference, options) {
1875
+ return await VaultFile.fromVaultReference({
1680
1876
  reference,
1681
1877
  config
1682
1878
  }, { signal: options?.signal });
1683
1879
  }
1684
1880
  async function createFromStream(name, contentLength, options) {
1685
- return VaultFile.fromStream({
1881
+ return await VaultFile.fromStream({
1686
1882
  name,
1687
1883
  contentLength,
1688
1884
  config,
@@ -1690,7 +1886,14 @@ function vaultClient(vaultConfig) {
1690
1886
  parentId: options?.parentId
1691
1887
  }, { signal: options?.signal });
1692
1888
  }
1693
- return { createFromContent, createFromReference, createFromStream };
1889
+ async function createFromContentBulk(files, options) {
1890
+ return await VaultFile.fromContentBulk({
1891
+ files,
1892
+ config,
1893
+ upload: options?.upload
1894
+ }, { signal: options?.signal });
1895
+ }
1896
+ return { createFromContent, createFromReference, createFromStream, createFromContentBulk };
1694
1897
  }
1695
1898
 
1696
- export { APIKeyAuthStrategy, DataTokenAuthStrategy, FetchError, VaultFile, convertS3UrlToVaultReference, extractVaultFileIdFromS3Url, isS3UrlExpired, isVaultFileS3Url, vaultClient };
1899
+ export { APIKeyAuthStrategy, DataTokenAuthStrategy, FetchError, VaultFile, convertS3UrlToVaultReference, extractVaultFileIdFromS3Url, getVaultParamsFromS3Url, isS3UrlExpired, isTaggedVaultPresignedUrl, isPresignedS3Url as isVaultFileS3Url, isVaultReference, vaultClient };
package/package.json CHANGED
@@ -1,49 +1,49 @@
1
1
  {
2
- "name": "@meistrari/vault-sdk",
3
- "version": "1.8.3",
4
- "license": "UNLICENSED",
5
- "repository": {
6
- "type": "git",
7
- "url": "https://github.com/meistrari/vault.git"
8
- },
9
- "exports": {
10
- ".": {
11
- "types": "./dist/index.d.ts",
12
- "import": "./dist/index.mjs",
13
- "require": "./dist/index.cjs"
2
+ "name": "@meistrari/vault-sdk",
3
+ "version": "2.0.0",
4
+ "license": "UNLICENSED",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "https://github.com/meistrari/vault.git"
8
+ },
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.mjs",
13
+ "require": "./dist/index.cjs"
14
+ }
15
+ },
16
+ "main": "dist/index.mjs",
17
+ "types": "dist/index.d.ts",
18
+ "files": [
19
+ "dist"
20
+ ],
21
+ "scripts": {
22
+ "test": "vitest --no-watch",
23
+ "test:watch": "vitest",
24
+ "build": "unbuild",
25
+ "lint": "eslint .",
26
+ "lint:fix": "eslint . --fix",
27
+ "check": "bun run lint && bun tsc --noEmit"
28
+ },
29
+ "dependencies": {
30
+ "@meistrari/file-type": "22.0.0",
31
+ "@meistrari/vault-shared": "workspace:*",
32
+ "mime-types": "3.0.1",
33
+ "ofetch": "1.4.1",
34
+ "zod": "3.23.8"
35
+ },
36
+ "devDependencies": {
37
+ "@types/bun": "latest",
38
+ "@types/mime-types": "3.0.1",
39
+ "msw": "2.6.8",
40
+ "unbuild": "2.0.0",
41
+ "vitest": "2.1.9"
42
+ },
43
+ "peerDependencies": {
44
+ "typescript": "^5.0.0"
45
+ },
46
+ "publishConfig": {
47
+ "access": "public"
14
48
  }
15
- },
16
- "main": "dist/index.mjs",
17
- "types": "dist/index.d.ts",
18
- "files": [
19
- "dist"
20
- ],
21
- "scripts": {
22
- "test": "vitest --no-watch",
23
- "test:watch": "vitest",
24
- "build": "unbuild",
25
- "lint": "eslint .",
26
- "lint:fix": "eslint . --fix",
27
- "check": "bun run lint && bun tsc --noEmit"
28
- },
29
- "dependencies": {
30
- "@meistrari/vault-shared": "0.0.7",
31
- "@meistrari/file-type": "22.0.0",
32
- "mime-types": "3.0.1",
33
- "ofetch": "1.4.1",
34
- "zod": "3.23.8"
35
- },
36
- "devDependencies": {
37
- "@types/bun": "latest",
38
- "@types/mime-types": "3.0.1",
39
- "msw": "2.6.8",
40
- "unbuild": "2.0.0",
41
- "vitest": "2.1.9"
42
- },
43
- "peerDependencies": {
44
- "typescript": "^5.0.0"
45
- },
46
- "publishConfig": {
47
- "access": "public"
48
- }
49
- }
49
+ }