microsoft/typespec

Public

mirrored from https://github.com/microsoft/typespecAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
ce9c567e5bfb441bb6415699a6b6fa797bc08f2e

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

packages/bundle-uploader/src/upload-browser-package.ts

118lines · modecode

1import { TokenCredential } from "@azure/identity";
2import {
3 AnonymousCredential,
4 BlobServiceClient,
5 ContainerClient,
6 StorageSharedKeyCredential,
7} from "@azure/storage-blob";
8import { BundleManifest, TypeSpecBundle, TypeSpecBundleFile } from "@typespec/bundler";
9import { join } from "path/posix";
10import { pkgsContainer, storageAccountName } from "./constants.js";
11
12export interface UploadBundleResult {
13 status: "uploaded" | "already-exists";
14 /** Resolve imports with absolute url. */
15 imports: Record<string, string>;
16}
17
18export interface PackageIndex {
19 version: string;
20 imports: Record<string, string>;
21}
22
23export class TypeSpecBundledPackageUploader {
24 #container: ContainerClient;
25
26 constructor(credential?: StorageSharedKeyCredential | AnonymousCredential | TokenCredential) {
27 this.#container = getCoverageContainer(storageAccountName, credential);
28 }
29
30 async createIfNotExists() {
31 await this.#container.createIfNotExists({
32 access: "blob",
33 });
34 }
35
36 async upload({ manifest, files }: TypeSpecBundle): Promise<UploadBundleResult> {
37 const imports = Object.fromEntries(
38 Object.entries(manifest.imports).map(([key, value]) => {
39 return [
40 key,
41 this.#container.url + "/" + normalizePath(join(manifest.name, manifest.version, value)),
42 ];
43 })
44 );
45 const created = await this.#uploadManifest(manifest);
46 if (!created) {
47 return { status: "already-exists", imports };
48 }
49 for (const file of files) {
50 await this.#uploadJsFile(manifest.name, manifest.version, file);
51 }
52 return { status: "uploaded", imports };
53 }
54
55 async uploadIndex(name: string, index: PackageIndex) {
56 const blob = this.#container.getBlockBlobClient(`indexes/${name}/${index.version}.json`);
57 const content = JSON.stringify(index);
58 await blob.upload(content, content.length, {
59 blobHTTPHeaders: {
60 blobContentType: "application/json; charset=utf-8",
61 },
62 });
63 }
64
65 async #uploadManifest(manifest: BundleManifest) {
66 try {
67 const blob = this.#container.getBlockBlobClient(
68 normalizePath(join(manifest.name, manifest.version, "manifest.json"))
69 );
70 const content = JSON.stringify(manifest);
71 await blob.upload(content, content.length, {
72 blobHTTPHeaders: {
73 blobContentType: "application/json; charset=utf-8",
74 },
75 conditions: {
76 ifNoneMatch: "*",
77 },
78 });
79 } catch (e: any) {
80 if (e.code === "BlobAlreadyExists") {
81 return false;
82 }
83 throw e;
84 }
85 return true;
86 }
87
88 async #uploadJsFile(pkgName: string, version: string, file: TypeSpecBundleFile) {
89 const blob = this.#container.getBlockBlobClient(
90 normalizePath(join(pkgName, version, file.filename))
91 );
92 const content = file.content;
93 await blob.upload(content, content.length, {
94 blobHTTPHeaders: {
95 blobContentType: "application/javascript; charset=utf-8",
96 },
97 conditions: {
98 ifNoneMatch: "*",
99 },
100 });
101 }
102}
103
104function getCoverageContainer(
105 storageAccountName: string,
106 credential?: StorageSharedKeyCredential | AnonymousCredential | TokenCredential
107): ContainerClient {
108 const blobSvc = new BlobServiceClient(
109 `https://${storageAccountName}.blob.core.windows.net`,
110 credential
111 );
112 const containerClient = blobSvc.getContainerClient(pkgsContainer);
113 return containerClient;
114}
115
116function normalizePath(path: string): string {
117 return path.replace(/\\/g, "/");
118}