@engineers/gcloud-storage 0.0.1 → 0.0.8-temp
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/index.d.ts +48 -0
- package/index.js +297 -0
- package/index.spec.d.ts +1 -0
- package/package.json +7 -35
- package/README.md +0 -49
- package/dist/index.js +0 -102984
package/index.d.ts
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
/// <reference types="node" />
|
|
3
|
+
import { Bucket, CreateBucketRequest, File, SaveOptions, Storage, UploadResponse, StorageOptions as _StorageOptions } from '@google-cloud/storage';
|
|
4
|
+
import { PathLike } from 'node:fs';
|
|
5
|
+
export interface StorageOptions extends _StorageOptions {
|
|
6
|
+
bucket: string;
|
|
7
|
+
rootDir?: string;
|
|
8
|
+
}
|
|
9
|
+
export interface UploadOptions {
|
|
10
|
+
destination: string;
|
|
11
|
+
}
|
|
12
|
+
export interface DownloadOptions {
|
|
13
|
+
destination?: string;
|
|
14
|
+
encoding?: BufferEncoding | null;
|
|
15
|
+
}
|
|
16
|
+
export interface DeleteOptions {
|
|
17
|
+
ignoreNotFound?: boolean;
|
|
18
|
+
}
|
|
19
|
+
export interface ExistsOptions {
|
|
20
|
+
}
|
|
21
|
+
export interface CreateBucketMeta extends CreateBucketRequest {
|
|
22
|
+
labels?: {
|
|
23
|
+
[key: string]: string;
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
export default class {
|
|
27
|
+
storage: Storage;
|
|
28
|
+
bucket: Bucket;
|
|
29
|
+
private baseDir;
|
|
30
|
+
constructor(options: StorageOptions);
|
|
31
|
+
upload(path: PathLike, options?: UploadOptions | string): Promise<UploadResponse | void>;
|
|
32
|
+
download(file: PathLike | File, options?: DownloadOptions | string): Promise<Buffer | string | Array<any> | {
|
|
33
|
+
[key: string]: any;
|
|
34
|
+
} | void>;
|
|
35
|
+
downloadAll(destination: PathLike, directory?: PathLike, filter?: RegExp | ((file: string) => boolean), options?: {
|
|
36
|
+
start?: number;
|
|
37
|
+
end?: number;
|
|
38
|
+
}): Promise<{
|
|
39
|
+
[key: string]: boolean;
|
|
40
|
+
}>;
|
|
41
|
+
write(path: PathLike, content?: any, options?: SaveOptions): Promise<any>;
|
|
42
|
+
read(file: PathLike | File, options?: DownloadOptions | string): Promise<string | void | any[] | Buffer | {
|
|
43
|
+
[key: string]: any;
|
|
44
|
+
}>;
|
|
45
|
+
delete(path: PathLike, options?: DeleteOptions): Promise<any>;
|
|
46
|
+
exists(path: PathLike, options?: ExistsOptions): Promise<boolean>;
|
|
47
|
+
create(metaData?: CreateBucketMeta): Promise<import("@google-cloud/storage").CreateBucketResponse>;
|
|
48
|
+
}
|
package/index.js
ADDED
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
import {
|
|
3
|
+
Storage
|
|
4
|
+
} from "@google-cloud/storage";
|
|
5
|
+
import { mkdirSync } from "node:fs";
|
|
6
|
+
import { dirname, resolve } from "node:path";
|
|
7
|
+
import stripJsonComments from "strip-json-comments";
|
|
8
|
+
import { URL } from "node:url";
|
|
9
|
+
|
|
10
|
+
// ../javascript/src/objects.ts
|
|
11
|
+
function objectType(object) {
|
|
12
|
+
return Object.prototype.toString.call(object).replace("[object ", "").replace("]", "").toLowerCase();
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// ../javascript/src/string.ts
|
|
16
|
+
function cleanString(value, options = {}) {
|
|
17
|
+
let opts = {
|
|
18
|
+
// remove '//' comments
|
|
19
|
+
// it is better to disable this option to avoid removing comment-like strings by mistake
|
|
20
|
+
// suc as `https://`
|
|
21
|
+
removeComments: false,
|
|
22
|
+
// remove break lines for multi-line string
|
|
23
|
+
removeBreakLines: true,
|
|
24
|
+
// trim each line of the multi-line string
|
|
25
|
+
trim: false,
|
|
26
|
+
...options
|
|
27
|
+
};
|
|
28
|
+
if (opts.removeComments)
|
|
29
|
+
value = value.replace(/\s*\/\/.*$/gm, "");
|
|
30
|
+
if (opts.removeBreakLines)
|
|
31
|
+
value = value.split("\n").map((line) => opts.trim ? line.trim() : line).join("");
|
|
32
|
+
if (opts.trim)
|
|
33
|
+
value = value.trim();
|
|
34
|
+
return value;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// ../javascript/src/regex.ts
|
|
38
|
+
function escape(value) {
|
|
39
|
+
return value.replace(/[$()*+./?[\\\]^{|}]/g, "\\$&");
|
|
40
|
+
}
|
|
41
|
+
function clean(value, options = {}) {
|
|
42
|
+
value = cleanString(value, options);
|
|
43
|
+
return options.escape ? escape(value) : value;
|
|
44
|
+
}
|
|
45
|
+
function toRegExp(value, optionsOrFlags = {}) {
|
|
46
|
+
let opts = {
|
|
47
|
+
delimiter: "",
|
|
48
|
+
...typeof optionsOrFlags === "string" ? { flags: optionsOrFlags } : optionsOrFlags
|
|
49
|
+
};
|
|
50
|
+
if (typeof value === "string") {
|
|
51
|
+
value = clean(value, opts);
|
|
52
|
+
} else if (Array.isArray(value)) {
|
|
53
|
+
value = value.map(
|
|
54
|
+
(element) => (
|
|
55
|
+
// we don't need to escape a RegExp pattern, only escape strings
|
|
56
|
+
`(?:${element instanceof RegExp ? element.source : clean(element.toString(), opts)})`
|
|
57
|
+
)
|
|
58
|
+
).join(opts.delimiter);
|
|
59
|
+
} else if (value instanceof RegExp) {
|
|
60
|
+
} else {
|
|
61
|
+
throw new TypeError(`value ${value} must be of type Pattern`);
|
|
62
|
+
}
|
|
63
|
+
return new RegExp(value, opts.flags);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// ../javascript/src/patterns.ts
|
|
67
|
+
var ipv4 = /^(?:(?:25[0-5]|2[0-4]\d|1\d\d|\d)\.?){4}$/;
|
|
68
|
+
var v6segment = "[a-fA-F\\d]{1,4}";
|
|
69
|
+
var ipv6 = toRegExp(
|
|
70
|
+
`(?:(?:${v6segment}:){7}(?:${v6segment}|:)|(?:${v6segment}:){6}(?:${ipv4}|:${v6segment}|:)|(?:${v6segment}:){5}(?::${ipv4}|(?::${v6segment}){1,2}|:)| (?:${v6segment}:){4}(?:(?::${v6segment}){0,1}:${ipv4}|(?::${v6segment}){1,3}|:)| (?:${v6segment}:){3}(?:(?::${v6segment}){0,2}:${ipv4}|(?::${v6segment}){1,4}|:)|(?:${v6segment}:){2}(?:(?::${v6segment}){0,3}:${ipv4}|(?::${v6segment}){1,5}|:)|(?:${v6segment}:){1}(?:(?::${v6segment}){0,4}:${ipv4}|(?::${v6segment}){1,6}|:)|(?::(?:(?::${v6segment}){0,5}:${ipv4}|(?::${v6segment}){1,7}|:)))(?:%[0-9a-zA-Z]{1,})?`,
|
|
71
|
+
{ removeComments: false }
|
|
72
|
+
);
|
|
73
|
+
var ip = toRegExp([ipv4, ipv6], { delimiter: "|" });
|
|
74
|
+
var rootDomain = /^([\dA-Za-z][\dA-Za-z-]{2,62})\.(\w{2,3})(?:\.([A-Za-z]{2}))?$/;
|
|
75
|
+
var domain = toRegExp(
|
|
76
|
+
[
|
|
77
|
+
// subdomain
|
|
78
|
+
// todo: RegExp captures the latest group match only, instead of capturing all of the repeating patterns
|
|
79
|
+
// https://stackoverflow.com/a/37004214/12577650
|
|
80
|
+
/^((?:([\dA-Za-z][\dA-Za-z-]{0,62})+\.)*)/,
|
|
81
|
+
// remove the first '^' from '^---' or '(?:^---)'
|
|
82
|
+
rootDomain.source.replace("^", "")
|
|
83
|
+
],
|
|
84
|
+
{ delimiter: "" }
|
|
85
|
+
);
|
|
86
|
+
var email = toRegExp([/^([a-z]+\w+)@/, domain.source.replace("^", "")], {
|
|
87
|
+
delimiter: ""
|
|
88
|
+
});
|
|
89
|
+
var unallowedUrlChars = ` "'\`
|
|
90
|
+
<>[\\]`;
|
|
91
|
+
var tdl = "com|net|org";
|
|
92
|
+
var link = toRegExp(
|
|
93
|
+
`((?:ftp|https?://[^${unallowedUrlChars}]+)|(?:[^${unallowedUrlChars}\\/]+\\.(?:${tdl})(?:\\.[a-z]{2}){0,1}))(?::d+)?(?:/[^${unallowedUrlChars}]+)*`,
|
|
94
|
+
{ flags: "gi" }
|
|
95
|
+
);
|
|
96
|
+
var plainLink = toRegExp(
|
|
97
|
+
[
|
|
98
|
+
link,
|
|
99
|
+
// doesn't followed by '*</a>', i.e: doesn't exist inside <a> tag (plain links only)
|
|
100
|
+
// https://stackoverflow.com/a/68188492/12577650
|
|
101
|
+
`(?![^<]*</a>)`
|
|
102
|
+
],
|
|
103
|
+
{ delimiter: "" }
|
|
104
|
+
);
|
|
105
|
+
|
|
106
|
+
// src/index.ts
|
|
107
|
+
var src_default = class {
|
|
108
|
+
storage;
|
|
109
|
+
bucket;
|
|
110
|
+
baseDir;
|
|
111
|
+
/**
|
|
112
|
+
* creates a new bucket
|
|
113
|
+
*
|
|
114
|
+
* @function constructor
|
|
115
|
+
* @param options
|
|
116
|
+
* @param bucket
|
|
117
|
+
*/
|
|
118
|
+
// todo: if(bucket instanceof admin.Bucket)this.bucket=bucket
|
|
119
|
+
// constructor();
|
|
120
|
+
constructor(options) {
|
|
121
|
+
let opts = { ...options };
|
|
122
|
+
if (!opts.keyFile && !opts.keyFilename && !opts.credentials && process.env.gcloud_service_account_storage) {
|
|
123
|
+
opts.credentials = JSON.parse(process.env.gcloud_service_account_storage);
|
|
124
|
+
}
|
|
125
|
+
this.storage = new Storage(opts);
|
|
126
|
+
if (opts.bucket.includes("/")) {
|
|
127
|
+
let parts = opts.bucket.split("/");
|
|
128
|
+
opts.bucket = parts.shift();
|
|
129
|
+
this.baseDir = parts.join("/") + opts.rootDir ? "/" + opts.rootDir : "";
|
|
130
|
+
}
|
|
131
|
+
this.bucket = this.storage.bucket(opts.bucket);
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* uploads a file to the current bucket
|
|
135
|
+
*
|
|
136
|
+
* @function upload
|
|
137
|
+
* @param file file path
|
|
138
|
+
* @param path
|
|
139
|
+
* @param options UploadOptions object or destination path as string
|
|
140
|
+
* @returns Promise<UploadResponse>; //UploadResponse=[File, API request]
|
|
141
|
+
*/
|
|
142
|
+
// todo: upload / download a folder
|
|
143
|
+
upload(path, options) {
|
|
144
|
+
let opts = {
|
|
145
|
+
...typeof options === "string" ? { destination: options } : options || {}
|
|
146
|
+
};
|
|
147
|
+
if (this.baseDir && opts?.destination) {
|
|
148
|
+
opts.destination = `${this.baseDir}/${opts.destination}`;
|
|
149
|
+
}
|
|
150
|
+
return this.bucket.upload(path.toString(), opts);
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* downloads a file
|
|
154
|
+
*
|
|
155
|
+
* @function download
|
|
156
|
+
* @param file file path
|
|
157
|
+
* @param options DownloadOptions object or destination as string
|
|
158
|
+
* @returns Promise that resolves to:
|
|
159
|
+
* - void: if options.destination used
|
|
160
|
+
* - Buffer: if options.encoding is undefined
|
|
161
|
+
* - Array or plain object: if the file is json
|
|
162
|
+
* - string: otherwise
|
|
163
|
+
*/
|
|
164
|
+
// todo: if(!options.destination)return th content as Promise<Buffer | ...>
|
|
165
|
+
// otherwise write the content into a local destination and return boolean
|
|
166
|
+
download(file, options) {
|
|
167
|
+
if (typeof file === "string" || file instanceof Buffer || file instanceof URL) {
|
|
168
|
+
file = this.bucket.file(
|
|
169
|
+
this.baseDir ? `${this.baseDir}/${file.toString()}` : file.toString()
|
|
170
|
+
);
|
|
171
|
+
}
|
|
172
|
+
let opts = Object.assign(
|
|
173
|
+
{ encoding: null },
|
|
174
|
+
typeof options === "string" ? { destination: options } : options || {}
|
|
175
|
+
);
|
|
176
|
+
if (opts.destination) {
|
|
177
|
+
mkdirSync(dirname(opts.destination), { recursive: true });
|
|
178
|
+
}
|
|
179
|
+
return file.download({ ...opts }).then((result) => {
|
|
180
|
+
if (opts.destination) {
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
let data = result[0];
|
|
184
|
+
if (opts.encoding === void 0) {
|
|
185
|
+
return data;
|
|
186
|
+
}
|
|
187
|
+
let dataString = data.toString();
|
|
188
|
+
try {
|
|
189
|
+
return file.name.slice(-5) === ".json" ? JSON.parse(stripJsonComments(dataString)) : dataString;
|
|
190
|
+
} catch {
|
|
191
|
+
return dataString;
|
|
192
|
+
}
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* download all files from a directory that match the provided filter
|
|
197
|
+
*
|
|
198
|
+
* @param destination the local path to download the files to
|
|
199
|
+
* @param directory the directory to be downloaded
|
|
200
|
+
* @param filter a pattern or function to filter the files to be downloaded
|
|
201
|
+
* @param options
|
|
202
|
+
* @param options.start download all files starting from the specified one (paginating behavior) (default is the first file)
|
|
203
|
+
* @param options.end download all files until the specified file (default is the last file)
|
|
204
|
+
* @returns a map of {file: result}
|
|
205
|
+
*/
|
|
206
|
+
downloadAll(destination, directory, filter, options = {}) {
|
|
207
|
+
options = Object.assign({ start: 0, end: void 0 }, options || {});
|
|
208
|
+
let results = {};
|
|
209
|
+
if (!filter) {
|
|
210
|
+
filter = (file) => true;
|
|
211
|
+
} else if (filter instanceof RegExp) {
|
|
212
|
+
filter = (file) => filter.test(file);
|
|
213
|
+
}
|
|
214
|
+
if (this.baseDir) {
|
|
215
|
+
directory = `${this.baseDir}/${directory}`;
|
|
216
|
+
}
|
|
217
|
+
return this.bucket.getFiles({ prefix: directory?.toString() }).then((files) => files[0].filter((file) => filter(file.name))).then(
|
|
218
|
+
(files) => Promise.all(
|
|
219
|
+
files.slice(options.start, options.end).map(
|
|
220
|
+
(file) => this.download(file, {
|
|
221
|
+
destination: resolve(`${destination.toString()}/${file.name}`)
|
|
222
|
+
}).then((result) => {
|
|
223
|
+
results[file.name] = true;
|
|
224
|
+
}).catch((error) => {
|
|
225
|
+
results[file.name] = false;
|
|
226
|
+
})
|
|
227
|
+
)
|
|
228
|
+
).then(() => results)
|
|
229
|
+
);
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* writes a content to a file in the bucket
|
|
233
|
+
*
|
|
234
|
+
* @param path bucket path
|
|
235
|
+
* @param content the content to be written. if no content provided, an empty file is created
|
|
236
|
+
* @param options
|
|
237
|
+
* @returns the original content
|
|
238
|
+
*/
|
|
239
|
+
write(path, content = "", options) {
|
|
240
|
+
if (this.baseDir) {
|
|
241
|
+
path = `${this.baseDir}/${path}`;
|
|
242
|
+
}
|
|
243
|
+
return this.bucket.file(path.toString()).save(
|
|
244
|
+
["array", "object"].includes(objectType(content)) ? JSON.stringify(content) : content,
|
|
245
|
+
options
|
|
246
|
+
).then(() => content);
|
|
247
|
+
}
|
|
248
|
+
/** an alias to download() to be compatible with node:fs functions */
|
|
249
|
+
read(file, options) {
|
|
250
|
+
return this.download(file, options);
|
|
251
|
+
}
|
|
252
|
+
// eslint-disable-next-line no-secrets/no-secrets
|
|
253
|
+
// https://stackoverflow.com/a/64539948/12577650
|
|
254
|
+
delete(path, options) {
|
|
255
|
+
if (this.baseDir) {
|
|
256
|
+
path = `${this.baseDir}/${path}`;
|
|
257
|
+
}
|
|
258
|
+
return this.bucket.file(path.toString()).delete(options);
|
|
259
|
+
}
|
|
260
|
+
/**
|
|
261
|
+
* check if a file exists in the bucket
|
|
262
|
+
* this method works with files only, not folders
|
|
263
|
+
* folders doesn't actually exist
|
|
264
|
+
* https://cloud.google.com/storage/docs/folders
|
|
265
|
+
*
|
|
266
|
+
* @param path file path
|
|
267
|
+
* @param options
|
|
268
|
+
* @returns boolean
|
|
269
|
+
*/
|
|
270
|
+
exists(path, options) {
|
|
271
|
+
if (this.baseDir) {
|
|
272
|
+
path = `/${this.baseDir}/${path}`;
|
|
273
|
+
}
|
|
274
|
+
return this.bucket.file(path.toString()).exists(options).then((result) => result[0]);
|
|
275
|
+
}
|
|
276
|
+
/**
|
|
277
|
+
* create a new bucket, and optionally add labels to it
|
|
278
|
+
* add the desired labels as key-value pairs to metaData.labels
|
|
279
|
+
* https://cloud.google.com/storage/docs/using-bucket-labels#storage-modify-bucket-label-nodejs
|
|
280
|
+
*
|
|
281
|
+
* @param metaData
|
|
282
|
+
* @returns
|
|
283
|
+
*/
|
|
284
|
+
create(metaData) {
|
|
285
|
+
let bucketName = this.bucket.name;
|
|
286
|
+
let { labels, ...meta } = metaData || {};
|
|
287
|
+
return this.storage.createBucket(bucketName, meta).then((result) => {
|
|
288
|
+
this.bucket.name = bucketName;
|
|
289
|
+
return labels ? this.bucket.setLabels(labels).then(() => result) : result;
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
// todo: read(file:string):Buffer
|
|
293
|
+
// todo: delete folder
|
|
294
|
+
};
|
|
295
|
+
export {
|
|
296
|
+
src_default as default
|
|
297
|
+
};
|
package/index.spec.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,42 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@engineers/gcloud-storage",
|
|
3
|
-
"version": "0.0.
|
|
4
|
-
"
|
|
5
|
-
"files": [
|
|
6
|
-
"dist"
|
|
7
|
-
],
|
|
8
|
-
"description": "google cloud storage",
|
|
3
|
+
"version": "0.0.8-temp",
|
|
4
|
+
"type": "module",
|
|
9
5
|
"private": false,
|
|
6
|
+
"description": "google cloud storage",
|
|
10
7
|
"dependencies": {
|
|
11
|
-
"@google-cloud/storage": "^
|
|
12
|
-
|
|
13
|
-
"repository": {
|
|
14
|
-
"type": "git",
|
|
15
|
-
"url": "https://github.com/eng-dibo/dibo.git"
|
|
16
|
-
},
|
|
17
|
-
"homepage": "https://github.com/eng-dibo/dibo#readme",
|
|
18
|
-
"bugs": {
|
|
19
|
-
"url": "https://github.com/eng-dibo/dibo/issues",
|
|
20
|
-
"email": "sh.eldeeb.2010+github@gmail.com"
|
|
21
|
-
},
|
|
22
|
-
"license": "MIT",
|
|
23
|
-
"author": "Sherif Eldeeb <sh.eldeeb.2010+github@gmail.com> (https://github.com/eng-dibo)",
|
|
24
|
-
"funding": [
|
|
25
|
-
{
|
|
26
|
-
"type": "paypal",
|
|
27
|
-
"url": "https://paypal.me/group99001"
|
|
28
|
-
},
|
|
29
|
-
{
|
|
30
|
-
"type": "patreon",
|
|
31
|
-
"url": "https://www.patreon.com/GoogleDev"
|
|
32
|
-
}
|
|
33
|
-
],
|
|
34
|
-
"scripts": {
|
|
35
|
-
"build": "webpack",
|
|
36
|
-
"_publish": "npm run build && npm publish --access=public"
|
|
8
|
+
"@google-cloud/storage": "^6.10.1",
|
|
9
|
+
"strip-json-comments": "^5.0.1"
|
|
37
10
|
},
|
|
38
|
-
"
|
|
39
|
-
"
|
|
40
|
-
"semantic-release-monorepo": "^7.0.5"
|
|
11
|
+
"imports": {
|
|
12
|
+
"#*": "./*"
|
|
41
13
|
}
|
|
42
14
|
}
|
package/README.md
DELETED
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
# @engineers/gcloud-storage
|
|
2
|
-
|
|
3
|
-
google cloud storage
|
|
4
|
-
|
|
5
|
-
## install
|
|
6
|
-
|
|
7
|
-
install the package and it's peer dependencies:
|
|
8
|
-
|
|
9
|
-
```
|
|
10
|
-
npm i @engineers/gcloud-storage
|
|
11
|
-
```
|
|
12
|
-
|
|
13
|
-
you can build this package and any of our packages yourself from [source](https://github.com/eng-dibo/dibo/tree/main/packages)
|
|
14
|
-
|
|
15
|
-
## contributing
|
|
16
|
-
|
|
17
|
-
contributing with us are very welcome.
|
|
18
|
-
|
|
19
|
-
read our [contributing guide](https://github.com/eng-dibo/dibo/blob/main/CONTRIBUTING.md)
|
|
20
|
-
|
|
21
|
-
## support us
|
|
22
|
-
|
|
23
|
-
- [paypal](https://paypal.me/group99001)
|
|
24
|
-
|
|
25
|
-
- [patreon](https://www.patreon.com/GoogleDev)
|
|
26
|
-
|
|
27
|
-
## useful packages by `@engineers`
|
|
28
|
-
|
|
29
|
-
- check out these useful packages that created by [@engineers organization](https://www.npmjs.com/org/engineers)
|
|
30
|
-
|
|
31
|
-
- [cache](https://github.com/eng-dibo/dibo/tree/main/packages/cache)
|
|
32
|
-
- [databases](https://github.com/eng-dibo/dibo/tree/main/packages/databases)
|
|
33
|
-
- [dibo-system](https://github.com/eng-dibo/dibo/tree/main/packages/dibo-system)
|
|
34
|
-
- [dom](https://github.com/eng-dibo/dibo/tree/main/packages/dom)
|
|
35
|
-
- [express-redirect-middleware](https://github.com/eng-dibo/dibo/tree/main/packages/express-redirect-middleware)
|
|
36
|
-
- [firebase-admin](https://github.com/eng-dibo/dibo/tree/main/packages/firebase-admin)
|
|
37
|
-
- [gcloud-storage](https://github.com/eng-dibo/dibo/tree/main/packages/gcloud-storage)
|
|
38
|
-
- [graphics](https://github.com/eng-dibo/dibo/tree/main/packages/graphics)
|
|
39
|
-
- [javascript](https://github.com/eng-dibo/dibo/tree/main/packages/javascript)
|
|
40
|
-
- [lazy-load](https://github.com/eng-dibo/dibo/tree/main/packages/lazy-load)
|
|
41
|
-
- [mongoose](https://github.com/eng-dibo/dibo/tree/main/packages/mongoose)
|
|
42
|
-
- [ngx-content-core](https://github.com/eng-dibo/dibo/tree/main/packages/ngx-content-core)
|
|
43
|
-
- [ngx-content-view-mat](https://github.com/eng-dibo/dibo/tree/main/packages/ngx-content-view-mat)
|
|
44
|
-
- [ngx-universal-express](https://github.com/eng-dibo/dibo/tree/main/packages/ngx-universal-express)
|
|
45
|
-
- [ngx-utils](https://github.com/eng-dibo/dibo/tree/main/packages/ngx-utils)
|
|
46
|
-
- [nodejs](https://github.com/eng-dibo/dibo/tree/main/packages/nodejs)
|
|
47
|
-
- [rxjs](https://github.com/eng-dibo/dibo/tree/main/packages/rxjs)
|
|
48
|
-
- [tmpl](https://github.com/eng-dibo/dibo/tree/main/packages/tmpl)
|
|
49
|
-
- [webpack](https://github.com/eng-dibo/dibo/tree/main/packages/webpack)
|