Skip to content

Commit 444ea38

Browse files
committed
feat: adding polyfill for Storage API for local server
1 parent 734a0ca commit 444ea38

File tree

6 files changed

+170
-2
lines changed

6 files changed

+170
-2
lines changed

lib/build/bundlers/esbuild/plugins/node-polyfills/index.js

+3-1
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,9 @@ const ESBuildNodeModulePlugin = (buildProd) => {
4949
options.external = options.external || [];
5050
[...polyfillManager.external].forEach(([key]) => {
5151
options.external.push(key);
52-
options.external.push(`node:${key}`);
52+
if (!key.includes('azion:')) {
53+
options.external.push(`node:${key}`);
54+
}
5355
});
5456
}
5557

lib/build/bundlers/polyfills/polyfills-manager.js

+4
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,10 @@ class PolyfillsManager {
150150
'async_hooks',
151151
`${externalPolyfillsPath}/async_hooks/index.js`,
152152
);
153+
this.setExternal(
154+
'azion:storage',
155+
`${externalPolyfillsPath}/azion/storage/index.js`,
156+
);
153157

154158
return {
155159
libs: this.libs,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
/* eslint-disable */
2+
import fs from 'fs';
3+
import path from 'path';
4+
import mime from 'mime-types';
5+
import { pipeline } from 'stream/promises';
6+
7+
class StorageContext {
8+
#pathBucket;
9+
constructor(bucketName, storagePath) {
10+
this.bucketName = bucketName;
11+
const _storagePath = storagePath || '.edge/storage';
12+
this.#pathBucket = `${_storagePath}/${bucketName}`;
13+
}
14+
15+
async get(key, options) {
16+
try {
17+
const item = await fs.promises.readFile(`${this.#pathBucket}/${key}`);
18+
return StorageContext.makeAsset(key, item);
19+
} catch (error) {
20+
throw error;
21+
}
22+
}
23+
24+
async put(key, value, options) {
25+
try {
26+
const prefix = path.dirname(key);
27+
await fs.promises.mkdir(`${this.#pathBucket}${prefix}`, {
28+
recursive: true,
29+
});
30+
31+
if (value instanceof ReadableStream) {
32+
const writeStream = fs.createWriteStream(`${this.#pathBucket}${key}`);
33+
await pipeline(value, writeStream);
34+
} else {
35+
await fs.promises.writeFile(`${this.#pathBucket}${key}`, value);
36+
}
37+
return StorageContext.makeAsset(key, value);
38+
} catch (error) {
39+
throw error;
40+
}
41+
}
42+
43+
async delete(key) {
44+
try {
45+
return await fs.promises.rm(`${this.#pathBucket}/${key}`);
46+
} catch (error) {
47+
throw error;
48+
}
49+
}
50+
51+
async list() {
52+
const pathBucketRoot = `${this.#pathBucket}/`;
53+
try {
54+
const files = await fs.promises.readdir(pathBucketRoot);
55+
const paths = files.map((file) => {
56+
return { key: path.join(pathBucketRoot, file) };
57+
});
58+
return { key_list: paths };
59+
} catch (error) {
60+
throw error;
61+
}
62+
}
63+
64+
static makeAsset(key, file) {
65+
const contentType = mime.lookup(key) || 'application/octet-stream';
66+
const contentLength = file.length;
67+
const metadata = {};
68+
return { contentRid: file, contentType, contentLength, metadata };
69+
}
70+
}
71+
72+
export default StorageContext;
+85
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
/* eslint-disable */
2+
3+
const PRIVATE_CONSTRUCTOR = Symbol('PRIVATE_CONSTRUCTOR');
4+
export default class Storage {
5+
#bucketName;
6+
constructor(bucketName) {
7+
if (typeof bucketName !== 'string') {
8+
throw new Error('bucketName must be a string');
9+
}
10+
this.#bucketName = bucketName;
11+
}
12+
13+
async get(key, options) {
14+
const asset = await new STORAGE_CONTEXT(this.#bucketName).get(key, options);
15+
return new StorageObject(asset, PRIVATE_CONSTRUCTOR);
16+
}
17+
18+
async put(key, value, options) {
19+
const asset = await new STORAGE_CONTEXT(this.#bucketName).put(
20+
key,
21+
value,
22+
options,
23+
);
24+
return new StorageObject(asset, PRIVATE_CONSTRUCTOR);
25+
}
26+
27+
async delete(key) {
28+
return await new STORAGE_CONTEXT(this.#bucketName).delete(key);
29+
}
30+
31+
async list(path) {
32+
let asset_list = await new STORAGE_CONTEXT(this.#bucketName).list();
33+
return new StorageObjectList(asset_list?.key_list, PRIVATE_CONSTRUCTOR);
34+
}
35+
}
36+
37+
export class StorageObject {
38+
#metadata;
39+
#contentType;
40+
#contentLength;
41+
#content;
42+
constructor(asset, privateConstructor) {
43+
if (privateConstructor !== PRIVATE_CONSTRUCTOR) {
44+
throw new Error('StorageObject constructor is private.');
45+
}
46+
let { contentRid, contentLength, contentType, metadata } = asset;
47+
48+
this.#content = contentRid;
49+
this.#contentLength = contentLength;
50+
51+
if (metadata === null) {
52+
// if metadata is null, set it to an empty Map.
53+
this.#metadata = new Map();
54+
} else {
55+
this.#metadata = new Map(Object.entries(metadata));
56+
}
57+
this.#contentType = contentType;
58+
}
59+
get content() {
60+
return this.#content.toString();
61+
}
62+
async arrayBuffer() {
63+
return this.#content;
64+
}
65+
get metadata() {
66+
return this.#metadata;
67+
}
68+
get contentType() {
69+
return this.#contentType;
70+
}
71+
get contentLength() {
72+
return this.#contentLength;
73+
}
74+
}
75+
76+
export class StorageObjectList {
77+
entries;
78+
79+
constructor(list, privateConstructor) {
80+
if (privateConstructor !== PRIVATE_CONSTRUCTOR) {
81+
throw new Error('StorageObjectList constructor is private.');
82+
}
83+
this.entries = list;
84+
}
85+
}

lib/env/polyfills/index.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import fetchPolyfill from './fetch/fetch.polyfills.js';
22
import FetchEventPolyfill from './fetch/FetchEvent.polyfills.js';
33
import AsyncHooks from './async_hooks/context/index.js';
4+
import StorageContext from './azion/storage/context/index.js';
45

5-
export { fetchPolyfill, FetchEventPolyfill, AsyncHooks };
6+
export { fetchPolyfill, FetchEventPolyfill, AsyncHooks, StorageContext };

lib/env/runtime.env.js

+4
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {
44
fetchPolyfill,
55
FetchEventPolyfill,
66
AsyncHooks,
7+
StorageContext,
78
} from './polyfills/index.js';
89

910
/**
@@ -63,6 +64,9 @@ function runtime(code) {
6364
// Async Hooks
6465
context.ASYNC_LOCAL_STORAGE = AsyncHooks;
6566

67+
// Storage Context
68+
context.STORAGE_CONTEXT = StorageContext;
69+
6670
return context;
6771
};
6872

0 commit comments

Comments
 (0)