diff --git a/README.md b/README.md
index 612d527..d96f646 100644
--- a/README.md
+++ b/README.md
@@ -111,7 +111,7 @@ Links
2. Import `BugSplatApiClient` and `VersionsApiClient` from @bugsplat/symbol-upload. Alternatively, you can import `OAuthClientCredentialsClient` if you'd prefer to authenticate with an [OAuth2 Client Credentials](https://docs.bugsplat.com/introduction/development/web-services/oauth2#client-credentials) Client ID and Client Secret.
```ts
-import { BugSplatApiClient, OAuthClientCredentialsClient, VersionsApiClient } from '@bugsplat/symbol-upload';
+import { BugSplatApiClient, OAuthClientCredentialsClient, uploadSymbolFiles } from '@bugsplat/symbol-upload';
```
3. Create a new instance of `BugSplatApiClient` using the `createAuthenticatedClientForNode` async factory function or `OAuthClientCredentialsClient` using the `createAuthenticatedClient` async factory function.
@@ -124,41 +124,16 @@ const bugsplat = await BugSplatApiClient.createAuthenticatedClientForNode(email,
const bugsplat = await OAuthClientCredentialsClient.createAuthenticatedClient(clientId, clientSecret);
```
-4. Create an `UploadableFile` object for each symbol file path.
+4. Upload your symbol files to bugsplat by calling the `uploadSymbolFiles` function.
```ts
-const files = paths.map(path => {
- const stat = fs.statSync(path);
- const size = stat.size;
- const name = basename(path);
- const file = fs.createReadStream(path);
- return {
- name,
- size,
- file
- };
-});
+const directory = '/path/to/symbols/dir';
+const files = '**/*.+(exe|dll|pdb)';
+await uploadSymbolFiles(bugsplat, database, application, version, directory, files);
```
-5. Create an instance of `VersionsApiClient` passing it an instance of `BugSplatApiClient`.
+If you've done everything correctly, your symbols should be shown by clicking the application link on the [Versions](https://app.bugsplat.com/v2/versions) page.
-```ts
-const versionsApiClient = new VersionsApiClient(bugsplat);
-```
-
-6. Await the call to `postSymbols` passing it the name of your BugSplat `database`, `application`, `version` and an array of `files`. These values need to match the values you used to initialize BugSplat on whichever [platform](https://docs.bugsplat.com/introduction/getting-started/integrations) you've integrated with.
-
-```ts
-await versionsApiClient.postSymbols(
- database,
- application,
- version,
- files
-);
-```
-
-If you've done everything correctly your symbols should now be shown on the [Versions](https://app.bugsplat.com/v2/versions) page.
-
-![Versions](https://bugsplat-public.s3.amazonaws.com/npm/symbol-upload/versions.png)
+
Thanks for using BugSplat!
diff --git a/bin/index.ts b/bin/index.ts
index 4bf9dc5..797c47c 100644
--- a/bin/index.ts
+++ b/bin/index.ts
@@ -1,20 +1,10 @@
#! /usr/bin/env node
-import { ApiClient, BugSplatApiClient, OAuthClientCredentialsClient, SymbolsApiClient, VersionsApiClient } from '@bugsplat/js-api-client';
+import { ApiClient, BugSplatApiClient, OAuthClientCredentialsClient, VersionsApiClient } from '@bugsplat/js-api-client';
import commandLineArgs, { CommandLineOptions } from 'command-line-args';
import commandLineUsage from 'command-line-usage';
-import { glob } from 'glob';
-import { existsSync } from 'node:fs';
-import { mkdir, readFile, stat } from 'node:fs/promises';
-import { basename, dirname, extname, join, relative } from 'node:path';
-import { pool } from 'workerpool';
+import { readFile, stat } from 'node:fs/promises';
+import { uploadSymbolFiles } from '../src/upload';
import { CommandLineDefinition, argDefinitions, usageDefinitions } from './command-line-definitions';
-import { SymbolFileInfo } from './info';
-import { tryGetPdbGuid, tryGetPeGuid } from './pdb';
-import { getSymFileInfo } from './sym';
-import { safeRemoveTmp, tmpDir } from './tmp';
-import { createWorkersFromSymbolFiles } from './worker';
-
-const workerPool = pool(join(__dirname, 'compression.js'));
(async () => {
let {
@@ -75,11 +65,10 @@ const workerPool = pool(join(__dirname, 'compression.js'));
console.log('Authentication success!');
- const versionsApiClient = new VersionsApiClient(bugsplat);
- const symbolsApiClient = new SymbolsApiClient(bugsplat);
-
if (remove) {
try {
+ const versionsApiClient = new VersionsApiClient(bugsplat);
+
console.log(`About to delete symbols for ${database}-${application}-${version}...`);
await versionsApiClient.deleteSymbols(
@@ -98,75 +87,15 @@ const workerPool = pool(join(__dirname, 'compression.js'));
}
directory = normalizeDirectory(directory);
- const globPattern = `${directory}/${files}`;
-
- let returnCode = 0;
- try {
- const symbolFilePaths = await glob(globPattern);
-
- if (!symbolFilePaths.length) {
- throw new Error(`Could not find any files to upload using glob ${globPattern}!`);
- }
-
- console.log(`Found files:\n ${symbolFilePaths.join('\n')}`);
- console.log(`About to upload symbols for ${database}-${application}-${version}...`);
-
- if (!existsSync(tmpDir)) {
- await mkdir(tmpDir);
- }
- const symbolFiles = await Promise.all(symbolFilePaths.map(async (symbolFilePath) => await createSymbolFileInfo(directory, symbolFilePath)));
- const workers = createWorkersFromSymbolFiles(workerPool, symbolFiles, [symbolsApiClient, versionsApiClient]);
- const uploads = workers.map((worker) => worker.upload(database, application, version));
- await Promise.all(uploads);
-
- console.log('Symbols uploaded successfully!');
- } catch (error) {
- console.error(error);
- returnCode = 1;
- } finally {
- await safeRemoveTmp();
- }
+ await uploadSymbolFiles(bugsplat, database, application, version, directory, files);
- process.exit(returnCode);
+ process.exit(0);
})().catch((error) => {
console.error(error.message);
process.exit(1);
});
-async function createSymbolFileInfo(searchDirectory: string, symbolFilePath: string): Promise {
- const path = symbolFilePath;
- const relativePath = relative(searchDirectory, dirname(path));
- const extLowerCase = extname(path).toLowerCase();
- const isSymFile = extLowerCase.includes('.sym');
- const isPdbFile = extLowerCase.includes('.pdb');
- const isPeFile = extLowerCase.includes('.exe') || extLowerCase.includes('.dll');
-
- let dbgId = '';
- let moduleName = '';
-
- if (isPdbFile) {
- dbgId = await tryGetPdbGuid(path);
- }
-
- if (isPeFile) {
- dbgId = await tryGetPeGuid(path);
- }
-
- if (isSymFile) {
- ({ dbgId, moduleName } = await getSymFileInfo(path));
- }
-
- moduleName = moduleName || basename(path);
-
- return {
- path,
- dbgId,
- moduleName,
- relativePath
- } as SymbolFileInfo;
-}
-
async function createBugSplatClient({
user,
password,
diff --git a/index.ts b/index.ts
index 88ac6c3..ca715a4 100644
--- a/index.ts
+++ b/index.ts
@@ -1,8 +1,11 @@
export {
+ ApiClient,
BugSplatApiClient,
VersionsApiClient,
SymbolsApiClient,
OAuthClientCredentialsClient,
UploadableFile,
GZippedSymbolFile
-} from '@bugsplat/js-api-client';
\ No newline at end of file
+} from '@bugsplat/js-api-client';
+
+export { uploadSymbolFiles } from './src/upload';
\ No newline at end of file
diff --git a/spec/compression.spec.ts b/spec/compression.spec.ts
index 907221a..98120df 100644
--- a/spec/compression.spec.ts
+++ b/spec/compression.spec.ts
@@ -6,7 +6,7 @@ import { join } from 'node:path';
import workerpool from 'workerpool';
import extract from 'extract-zip';
import { cwd } from 'node:process';
-const pool = workerpool.pool(join(__dirname, '../bin/compression.js'));
+const pool = workerpool.pool(join(__dirname, '../src/compression.js'));
describe('gzip', () => {
describe('createGzipFile', () => {
diff --git a/spec/pdb.spec.ts b/spec/pdb.spec.ts
index 424e12b..2f7f395 100644
--- a/spec/pdb.spec.ts
+++ b/spec/pdb.spec.ts
@@ -1,4 +1,4 @@
-import { tryGetPdbGuid, tryGetPeGuid } from '../bin/pdb';
+import { tryGetPdbGuid, tryGetPeGuid } from '../src/pdb';
describe('pdb', () => {
describe('tryGetPdbGuid', () => {
diff --git a/spec/sym.spec.ts b/spec/sym.spec.ts
index 4dac062..1126ced 100644
--- a/spec/sym.spec.ts
+++ b/spec/sym.spec.ts
@@ -1,4 +1,4 @@
-import { getSymFileInfo } from '../bin/sym';
+import { getSymFileInfo } from '../src/sym';
describe('getSymFileInfo', () => {
it('should get debug id for file with a 33 character debug id', async () => {
diff --git a/spec/tmp.spec.ts b/spec/tmp.spec.ts
index 02a0857..7dc324d 100644
--- a/spec/tmp.spec.ts
+++ b/spec/tmp.spec.ts
@@ -1,4 +1,4 @@
-import { safeRemoveTmp } from '../bin/tmp';
+import { safeRemoveTmp } from '../src/tmp';
describe('tmp', () => {
it('should retry removing tmp directory', async () => {
diff --git a/spec/worker.spec.ts b/spec/worker.spec.ts
index e1b0d3a..f14d907 100644
--- a/spec/worker.spec.ts
+++ b/spec/worker.spec.ts
@@ -1,7 +1,7 @@
import { SymbolsApiClient, VersionsApiClient } from '@bugsplat/js-api-client';
import retryPromise from 'promise-retry';
-import { SymbolFileInfo } from '../bin/info';
-import { UploadWorker, createWorkersFromSymbolFiles } from '../bin/worker';
+import { SymbolFileInfo } from '../src/info';
+import { UploadWorker, createWorkersFromSymbolFiles } from '../src/worker';
import { cpus } from 'node:os';
import { WorkerPool } from 'workerpool';
import { basename } from 'node:path';
diff --git a/bin/compression.js b/src/compression.js
similarity index 100%
rename from bin/compression.js
rename to src/compression.js
diff --git a/bin/info.ts b/src/info.ts
similarity index 100%
rename from bin/info.ts
rename to src/info.ts
diff --git a/bin/pdb.ts b/src/pdb.ts
similarity index 100%
rename from bin/pdb.ts
rename to src/pdb.ts
diff --git a/bin/sym.ts b/src/sym.ts
similarity index 100%
rename from bin/sym.ts
rename to src/sym.ts
diff --git a/bin/tmp.ts b/src/tmp.ts
similarity index 100%
rename from bin/tmp.ts
rename to src/tmp.ts
diff --git a/src/upload.ts b/src/upload.ts
new file mode 100644
index 0000000..a6a599a
--- /dev/null
+++ b/src/upload.ts
@@ -0,0 +1,76 @@
+import { ApiClient, SymbolsApiClient, VersionsApiClient } from "@bugsplat/js-api-client";
+import { existsSync } from "node:fs";
+import { mkdir } from "node:fs/promises";
+import { basename, dirname, extname, join, relative } from "node:path";
+import { pool } from "workerpool";
+import { SymbolFileInfo } from './info';
+import { tryGetPdbGuid, tryGetPeGuid } from './pdb';
+import { getSymFileInfo } from './sym';
+import { safeRemoveTmp, tmpDir } from './tmp';
+import { createWorkersFromSymbolFiles } from './worker';
+import { glob } from "glob";
+
+const workerPool = pool(join(__dirname, 'compression.js'));
+
+export async function uploadSymbolFiles(bugsplat: ApiClient, database: string, application: string, version: string, directory: string, filesGlob: string) {
+ try {
+ const globPattern = `${directory}/${filesGlob}`;
+
+ const symbolFilePaths = await glob(globPattern);
+
+ if (!symbolFilePaths.length) {
+ throw new Error(`Could not find any files to upload using glob ${globPattern}!`);
+ }
+
+ console.log(`Found files:\n ${symbolFilePaths.join('\n')}`);
+ console.log(`About to upload symbols for ${database}-${application}-${version}...`);
+
+ if (!existsSync(tmpDir)) {
+ await mkdir(tmpDir);
+ }
+
+ const symbolsApiClient = new SymbolsApiClient(bugsplat);
+ const versionsApiClient = new VersionsApiClient(bugsplat);
+ const symbolFiles = await Promise.all(symbolFilePaths.map(async (symbolFilePath) => await createSymbolFileInfo(directory, symbolFilePath)));
+ const workers = createWorkersFromSymbolFiles(workerPool, symbolFiles, [symbolsApiClient, versionsApiClient]);
+ const uploads = workers.map((worker) => worker.upload(database, application, version));
+ await Promise.all(uploads);
+
+ console.log('Symbols uploaded successfully!');
+ } finally {
+ await safeRemoveTmp();
+ }
+}
+
+async function createSymbolFileInfo(searchDirectory: string, symbolFilePath: string): Promise {
+ const path = symbolFilePath;
+ const relativePath = relative(searchDirectory, dirname(path));
+ const extLowerCase = extname(path).toLowerCase();
+ const isSymFile = extLowerCase.includes('.sym');
+ const isPdbFile = extLowerCase.includes('.pdb');
+ const isPeFile = extLowerCase.includes('.exe') || extLowerCase.includes('.dll');
+
+ let dbgId = '';
+ let moduleName = '';
+
+ if (isPdbFile) {
+ dbgId = await tryGetPdbGuid(path);
+ }
+
+ if (isPeFile) {
+ dbgId = await tryGetPeGuid(path);
+ }
+
+ if (isSymFile) {
+ ({ dbgId, moduleName } = await getSymFileInfo(path));
+ }
+
+ moduleName = moduleName || basename(path);
+
+ return {
+ path,
+ dbgId,
+ moduleName,
+ relativePath
+ } as SymbolFileInfo;
+}
\ No newline at end of file
diff --git a/bin/worker.ts b/src/worker.ts
similarity index 100%
rename from bin/worker.ts
rename to src/worker.ts
diff --git a/tsconfig.json b/tsconfig.json
index 60af9dc..105d7d8 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -15,6 +15,6 @@
"files": [
"./index.ts",
"./bin/index.ts",
- "./bin/compression.js"
+ "src/compression.js"
]
}
\ No newline at end of file