@certik/skynet 0.25.0 → 0.25.2
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/.vscode/settings.json +5 -0
- package/CHANGELOG.md +10 -0
- package/README.md +8 -2
- package/dist/abi.d.ts +111 -0
- package/dist/abi.js +571 -0
- package/dist/address.d.ts +2 -0
- package/dist/address.js +24 -0
- package/dist/api.d.ts +31 -0
- package/dist/api.js +260 -0
- package/dist/app.d.ts +101 -0
- package/dist/app.js +2077 -0
- package/dist/availability.d.ts +23 -0
- package/dist/availability.js +133 -0
- package/dist/cli.d.ts +5 -0
- package/dist/cli.js +41 -0
- package/dist/const.d.ts +34 -0
- package/dist/const.js +162 -0
- package/dist/date.d.ts +5 -0
- package/dist/date.js +56 -0
- package/dist/deploy.d.ts +75 -0
- package/dist/deploy.js +587 -0
- package/dist/dynamodb.d.ts +16 -0
- package/dist/dynamodb.js +479 -0
- package/dist/env.d.ts +6 -0
- package/dist/env.js +26 -0
- package/dist/goalert.d.ts +19 -0
- package/dist/goalert.js +43 -0
- package/dist/graphql.d.ts +6 -0
- package/dist/graphql.js +35 -0
- package/dist/indexer.d.ts +69 -0
- package/dist/indexer.js +1099 -0
- package/dist/log.d.ts +13 -0
- package/dist/log.js +63 -0
- package/dist/object-hash.d.ts +1 -0
- package/dist/object-hash.js +61 -0
- package/dist/por.d.ts +37 -0
- package/dist/por.js +120 -0
- package/dist/s3.d.ts +20 -0
- package/dist/s3.js +122 -0
- package/dist/search.d.ts +5 -0
- package/dist/search.js +105 -0
- package/dist/selector.d.ts +17 -0
- package/dist/selector.js +44 -0
- package/dist/slack.d.ts +14 -0
- package/dist/slack.js +29 -0
- package/dist/util.d.ts +4 -0
- package/dist/util.js +27 -0
- package/examples/api.ts +0 -0
- package/examples/indexer.ts +0 -0
- package/examples/mode-indexer.ts +0 -0
- package/package.json +1 -1
- package/src/deploy.ts +1 -1
package/dist/address.js
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// src/address.ts
|
|
2
|
+
function parseAddress(address) {
|
|
3
|
+
const separatorIndex = address.indexOf(":");
|
|
4
|
+
let protocolPart;
|
|
5
|
+
let addressPart;
|
|
6
|
+
if (separatorIndex === -1) {
|
|
7
|
+
protocolPart = "eth";
|
|
8
|
+
addressPart = address;
|
|
9
|
+
} else {
|
|
10
|
+
protocolPart = address.slice(0, separatorIndex);
|
|
11
|
+
addressPart = address.slice(separatorIndex + 1);
|
|
12
|
+
}
|
|
13
|
+
if (addressPart.startsWith("0x")) {
|
|
14
|
+
addressPart = addressPart.toLowerCase();
|
|
15
|
+
}
|
|
16
|
+
return [protocolPart, addressPart];
|
|
17
|
+
}
|
|
18
|
+
function composeAddress(protocol, addr) {
|
|
19
|
+
return `${protocol}:${addr}`;
|
|
20
|
+
}
|
|
21
|
+
export {
|
|
22
|
+
parseAddress,
|
|
23
|
+
composeAddress
|
|
24
|
+
};
|
package/dist/api.d.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import express from "express";
|
|
2
|
+
import type { Request, Response, NextFunction } from "express";
|
|
3
|
+
import type { Selector } from "./selector";
|
|
4
|
+
export type Middleware = (req: Request, res: Response, next: NextFunction) => Promise<void>;
|
|
5
|
+
type Route = {
|
|
6
|
+
method?: "GET" | "POST" | "PUT" | "DELETE" | "PATCH";
|
|
7
|
+
path: string;
|
|
8
|
+
middlewares?: Middleware[];
|
|
9
|
+
handler: (args: {
|
|
10
|
+
req: Request;
|
|
11
|
+
res: Response;
|
|
12
|
+
verbose?: boolean;
|
|
13
|
+
}) => Promise<void>;
|
|
14
|
+
protected?: boolean;
|
|
15
|
+
};
|
|
16
|
+
type Serve = {
|
|
17
|
+
prefix: string;
|
|
18
|
+
port: number;
|
|
19
|
+
apiKey?: string | Record<string, string>;
|
|
20
|
+
};
|
|
21
|
+
export declare function startApiApp({ binaryName, name, selector, routes, serve, beforeListen, }: {
|
|
22
|
+
binaryName: string;
|
|
23
|
+
name: string;
|
|
24
|
+
selector?: Selector;
|
|
25
|
+
routes: Route[];
|
|
26
|
+
serve: Serve;
|
|
27
|
+
beforeListen?: (args: {
|
|
28
|
+
app: ReturnType<typeof express>;
|
|
29
|
+
}) => Promise<void>;
|
|
30
|
+
}): Promise<void>;
|
|
31
|
+
export {};
|
package/dist/api.js
ADDED
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
// src/selector.ts
|
|
2
|
+
function getSelectorDesc(selector) {
|
|
3
|
+
return Object.keys(selector).map((name) => {
|
|
4
|
+
return ` --${name.padEnd(14)}${selector[name].desc || selector[name].description || ""}`;
|
|
5
|
+
}).join(`
|
|
6
|
+
`);
|
|
7
|
+
}
|
|
8
|
+
function getSelectorFlags(selector) {
|
|
9
|
+
return Object.keys(selector).reduce((acc, name) => {
|
|
10
|
+
const flag = {
|
|
11
|
+
type: selector[name].type || "string",
|
|
12
|
+
...selector[name]
|
|
13
|
+
};
|
|
14
|
+
if (!selector[name].optional && selector[name].isRequired !== false) {
|
|
15
|
+
flag.isRequired = true;
|
|
16
|
+
}
|
|
17
|
+
return { ...acc, [name]: flag };
|
|
18
|
+
}, {});
|
|
19
|
+
}
|
|
20
|
+
function toSelectorString(selectorFlags, delim = ",") {
|
|
21
|
+
return Object.keys(selectorFlags).sort().map((flag) => {
|
|
22
|
+
return `${flag}=${selectorFlags[flag]}`;
|
|
23
|
+
}).join(delim);
|
|
24
|
+
}
|
|
25
|
+
function normalizeSelectorValue(v) {
|
|
26
|
+
return v.replace(/[^A-Za-z0-9]+/g, "-");
|
|
27
|
+
}
|
|
28
|
+
function getJobName(name, selectorFlags, mode) {
|
|
29
|
+
const selectorNamePart = Object.keys(selectorFlags).sort().map((name2) => selectorFlags[name2]).join("-");
|
|
30
|
+
let jobName = name;
|
|
31
|
+
if (mode) {
|
|
32
|
+
jobName += `-${mode}`;
|
|
33
|
+
}
|
|
34
|
+
if (selectorNamePart.length > 0) {
|
|
35
|
+
jobName += `-${normalizeSelectorValue(selectorNamePart)}`;
|
|
36
|
+
}
|
|
37
|
+
return jobName;
|
|
38
|
+
}
|
|
39
|
+
// src/env.ts
|
|
40
|
+
function ensureAndGet(envName, defaultValue) {
|
|
41
|
+
return process.env[envName] || defaultValue;
|
|
42
|
+
}
|
|
43
|
+
function getEnvironment() {
|
|
44
|
+
return ensureAndGet("SKYNET_ENVIRONMENT", "dev");
|
|
45
|
+
}
|
|
46
|
+
function getEnvOrThrow(envName) {
|
|
47
|
+
if (!process.env[envName]) {
|
|
48
|
+
throw new Error(`Must set environment variable ${envName}`);
|
|
49
|
+
}
|
|
50
|
+
return process.env[envName];
|
|
51
|
+
}
|
|
52
|
+
function isProduction() {
|
|
53
|
+
return getEnvironment() === "prd";
|
|
54
|
+
}
|
|
55
|
+
function isDev() {
|
|
56
|
+
return getEnvironment() === "dev";
|
|
57
|
+
}
|
|
58
|
+
// src/log.ts
|
|
59
|
+
function isObject(a) {
|
|
60
|
+
return !!a && a.constructor === Object;
|
|
61
|
+
}
|
|
62
|
+
function print(o) {
|
|
63
|
+
if (Array.isArray(o)) {
|
|
64
|
+
return `[${o.map(print).join(", ")}]`;
|
|
65
|
+
}
|
|
66
|
+
if (isObject(o)) {
|
|
67
|
+
return `{${Object.keys(o).map((k) => `${k}: ${o[k]}`).join(", ")}}`;
|
|
68
|
+
}
|
|
69
|
+
return `${o}`;
|
|
70
|
+
}
|
|
71
|
+
function getLine(params) {
|
|
72
|
+
let line = "";
|
|
73
|
+
for (let i = 0, l = params.length;i < l; i++) {
|
|
74
|
+
line += `${print(params[i])} `.replace(/\n/gm, "\t");
|
|
75
|
+
}
|
|
76
|
+
return line.trim();
|
|
77
|
+
}
|
|
78
|
+
function timestamp() {
|
|
79
|
+
return new Date().toISOString();
|
|
80
|
+
}
|
|
81
|
+
var inline = {
|
|
82
|
+
debug: function(...args) {
|
|
83
|
+
if (true) {
|
|
84
|
+
console.log(`${timestamp()} ${getLine(args)}`);
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
log: function(...args) {
|
|
88
|
+
if (true) {
|
|
89
|
+
console.log(`${timestamp()} ${getLine(args)}`);
|
|
90
|
+
}
|
|
91
|
+
},
|
|
92
|
+
error: function(...args) {
|
|
93
|
+
if (true) {
|
|
94
|
+
console.error(`${timestamp()} ${getLine(args)}`);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
var logger = {
|
|
99
|
+
debug: function(...args) {
|
|
100
|
+
if (true) {
|
|
101
|
+
console.log(`[${timestamp()}]`, ...args);
|
|
102
|
+
}
|
|
103
|
+
},
|
|
104
|
+
log: function(...args) {
|
|
105
|
+
if (true) {
|
|
106
|
+
console.log(`[${timestamp()}]`, ...args);
|
|
107
|
+
}
|
|
108
|
+
},
|
|
109
|
+
error: function(...args) {
|
|
110
|
+
if (true) {
|
|
111
|
+
console.error(`[${timestamp()}]`, ...args);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
// src/api.ts
|
|
116
|
+
import osModule from "os";
|
|
117
|
+
import express from "express";
|
|
118
|
+
import meow from "meow";
|
|
119
|
+
async function logStartMiddleware(_, res, next) {
|
|
120
|
+
const start = new Date;
|
|
121
|
+
res.set("x-requested-at", start.toISOString());
|
|
122
|
+
next();
|
|
123
|
+
}
|
|
124
|
+
async function contextMiddleware(_, res, next) {
|
|
125
|
+
res.set("x-instance-id", osModule.hostname());
|
|
126
|
+
next();
|
|
127
|
+
}
|
|
128
|
+
async function logEndMiddleware(req, res, next) {
|
|
129
|
+
const requestedAt = res.get("x-requested-at");
|
|
130
|
+
if (!requestedAt) {
|
|
131
|
+
inline.log("missing x-requested-at header");
|
|
132
|
+
next();
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
const start = new Date(requestedAt).getTime();
|
|
136
|
+
const end = new Date().getTime();
|
|
137
|
+
const logInfo = {
|
|
138
|
+
start,
|
|
139
|
+
end,
|
|
140
|
+
elapsed: `${end - start}ms`,
|
|
141
|
+
endpoint: req.path,
|
|
142
|
+
host: req.hostname,
|
|
143
|
+
status: res.statusCode
|
|
144
|
+
};
|
|
145
|
+
if (res.statusMessage) {
|
|
146
|
+
logInfo.errorMessage = res.statusMessage;
|
|
147
|
+
}
|
|
148
|
+
inline.log(JSON.stringify(logInfo));
|
|
149
|
+
next();
|
|
150
|
+
}
|
|
151
|
+
var apiKeyMiddleware = (key) => {
|
|
152
|
+
async function requireAPIKey(req, res, next) {
|
|
153
|
+
try {
|
|
154
|
+
const apiKey = req.get("x-api-key") || req.query["api-key"];
|
|
155
|
+
if (!apiKey) {
|
|
156
|
+
inline.log("request without api key");
|
|
157
|
+
res.status(400).send("require x-api-key header");
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
if (typeof key === "string") {
|
|
161
|
+
if (apiKey !== key) {
|
|
162
|
+
inline.log("request has an invalid api key");
|
|
163
|
+
res.status(400).send("invalid api key");
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
inline.log(`requested by valid key ${key.slice(0, 6)}`);
|
|
167
|
+
} else {
|
|
168
|
+
const name = key[apiKey];
|
|
169
|
+
if (!name) {
|
|
170
|
+
inline.log("request has an invalid api key");
|
|
171
|
+
res.status(400).send("invalid api key");
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
inline.log(`requested by authorized user ${name}`);
|
|
175
|
+
}
|
|
176
|
+
next();
|
|
177
|
+
} catch (err) {
|
|
178
|
+
inline.log("check api key error", err);
|
|
179
|
+
res.status(500).send("internal error");
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
return requireAPIKey;
|
|
183
|
+
};
|
|
184
|
+
async function startApiApp({
|
|
185
|
+
binaryName,
|
|
186
|
+
name,
|
|
187
|
+
selector = {},
|
|
188
|
+
routes,
|
|
189
|
+
serve,
|
|
190
|
+
beforeListen
|
|
191
|
+
}) {
|
|
192
|
+
const app = express();
|
|
193
|
+
app.use(express.json({ limit: "20mb" }));
|
|
194
|
+
const cli = meow(`
|
|
195
|
+
Usage
|
|
196
|
+
$ ${binaryName} <options>
|
|
197
|
+
|
|
198
|
+
Options
|
|
199
|
+
${getSelectorDesc(selector)}
|
|
200
|
+
--verbose Output debug messages
|
|
201
|
+
`, {
|
|
202
|
+
importMeta: import.meta,
|
|
203
|
+
description: false,
|
|
204
|
+
flags: {
|
|
205
|
+
...getSelectorFlags(selector),
|
|
206
|
+
verbose: {
|
|
207
|
+
type: "boolean",
|
|
208
|
+
default: false
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
});
|
|
212
|
+
const { verbose, ...selectorFlags } = cli.flags;
|
|
213
|
+
for (const route of routes) {
|
|
214
|
+
const method = route.method ? route.method.toLowerCase() : "get";
|
|
215
|
+
const middlewares = route.middlewares || [];
|
|
216
|
+
if (route.protected) {
|
|
217
|
+
if (!serve.apiKey) {
|
|
218
|
+
throw new Error("serve.apiKey is required for protected route");
|
|
219
|
+
}
|
|
220
|
+
middlewares.unshift(apiKeyMiddleware(serve.apiKey));
|
|
221
|
+
}
|
|
222
|
+
if (app[method]) {
|
|
223
|
+
if (verbose) {
|
|
224
|
+
inline.log(`registering ${method} ${route.path}`);
|
|
225
|
+
}
|
|
226
|
+
app[method](route.path, contextMiddleware, logStartMiddleware, ...middlewares, async (req, res, next) => {
|
|
227
|
+
try {
|
|
228
|
+
await route.handler({ req, res, ...selectorFlags });
|
|
229
|
+
} catch (routeErr) {
|
|
230
|
+
if (routeErr instanceof Error) {
|
|
231
|
+
inline.log("caught route err", routeErr, routeErr.stack);
|
|
232
|
+
res.status(500).send(`internal server error: ${routeErr.message}`);
|
|
233
|
+
} else {
|
|
234
|
+
inline.log("caught route err", routeErr);
|
|
235
|
+
res.status(500).send("internal server error");
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
next();
|
|
239
|
+
}, logEndMiddleware);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
if (!routes.some((r) => r.path === "/" && r.method?.toUpperCase() === "GET")) {
|
|
243
|
+
app.get("/", (_, res) => {
|
|
244
|
+
res.send("ok");
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
if (beforeListen) {
|
|
248
|
+
await beforeListen({ app });
|
|
249
|
+
}
|
|
250
|
+
app.listen(serve.port, () => {
|
|
251
|
+
if (isProduction()) {
|
|
252
|
+
inline.log(`${name} listening at https://api.wf.corp.certik.com${serve.prefix}`);
|
|
253
|
+
} else {
|
|
254
|
+
inline.log(`${name} listening at http://localhost:${serve.port}`);
|
|
255
|
+
}
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
export {
|
|
259
|
+
startApiApp
|
|
260
|
+
};
|
package/dist/app.d.ts
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import type { IndexerStateValue, State, ModeBuildFunction, StatelessBuildFunction, ValidateFunction } from "./indexer";
|
|
2
|
+
import type { Env } from "./deploy";
|
|
3
|
+
import type { Middleware } from "./api";
|
|
4
|
+
import type { Selector } from "./selector";
|
|
5
|
+
import type { Request, Response, Application } from "express";
|
|
6
|
+
type RouteDefinition = {
|
|
7
|
+
method?: "GET" | "POST" | "PUT" | "DELETE" | "PATCH";
|
|
8
|
+
path: string;
|
|
9
|
+
middlewares?: Middleware[];
|
|
10
|
+
handler: (args: {
|
|
11
|
+
req: Request;
|
|
12
|
+
res: Response;
|
|
13
|
+
verbose?: boolean;
|
|
14
|
+
}) => Promise<void>;
|
|
15
|
+
protected?: boolean;
|
|
16
|
+
};
|
|
17
|
+
type ServeDefinition = {
|
|
18
|
+
prefix: string;
|
|
19
|
+
port: number;
|
|
20
|
+
apiKey?: string | Record<string, string>;
|
|
21
|
+
killTimeout?: string;
|
|
22
|
+
cpu: number;
|
|
23
|
+
mem: number;
|
|
24
|
+
instances?: number;
|
|
25
|
+
};
|
|
26
|
+
type BuildDefinition<TFunc> = {
|
|
27
|
+
func: TFunc;
|
|
28
|
+
killTimeout?: string;
|
|
29
|
+
maxRetry?: number;
|
|
30
|
+
cpu: number;
|
|
31
|
+
mem: number;
|
|
32
|
+
schedule: string;
|
|
33
|
+
restart?: {
|
|
34
|
+
attempts: number;
|
|
35
|
+
mode: string;
|
|
36
|
+
interval: string;
|
|
37
|
+
delay: string;
|
|
38
|
+
};
|
|
39
|
+
batchSize?: number;
|
|
40
|
+
concurrency?: number;
|
|
41
|
+
};
|
|
42
|
+
type ValidateDefinition<TFunc> = {
|
|
43
|
+
func: TFunc;
|
|
44
|
+
batchSize: number;
|
|
45
|
+
killTimeout?: string;
|
|
46
|
+
cpu: number;
|
|
47
|
+
mem: number;
|
|
48
|
+
concurrency?: number;
|
|
49
|
+
schedule: string;
|
|
50
|
+
};
|
|
51
|
+
declare function indexer<TSelector extends Selector>({ name, selector, build, env, region, }: {
|
|
52
|
+
name: string;
|
|
53
|
+
selector: TSelector;
|
|
54
|
+
build: BuildDefinition<StatelessBuildFunction<TSelector>>;
|
|
55
|
+
env?: Env;
|
|
56
|
+
region?: string;
|
|
57
|
+
}): () => Promise<void>;
|
|
58
|
+
declare function modeIndexer<T extends IndexerStateValue, TSelector extends Selector>({ name, selector, state, build, validate, env, region, }: {
|
|
59
|
+
name: string;
|
|
60
|
+
selector: TSelector;
|
|
61
|
+
state: State<T, TSelector>;
|
|
62
|
+
build: BuildDefinition<ModeBuildFunction<T, TSelector>>;
|
|
63
|
+
validate?: ValidateDefinition<ValidateFunction<TSelector>>;
|
|
64
|
+
env?: Env;
|
|
65
|
+
region?: string;
|
|
66
|
+
}): () => Promise<void>;
|
|
67
|
+
declare function api({ name, routes, serve, beforeListen, env, region, }: {
|
|
68
|
+
name: string;
|
|
69
|
+
routes: RouteDefinition[];
|
|
70
|
+
serve: ServeDefinition;
|
|
71
|
+
beforeListen?: ({ app }: {
|
|
72
|
+
app: Application;
|
|
73
|
+
}) => Promise<void>;
|
|
74
|
+
env?: Env;
|
|
75
|
+
region?: string;
|
|
76
|
+
}): () => Promise<void>;
|
|
77
|
+
declare const SENSITIVE_VALUE: null;
|
|
78
|
+
declare const every: (n?: number) => {
|
|
79
|
+
second: string;
|
|
80
|
+
seconds: string;
|
|
81
|
+
minute: string;
|
|
82
|
+
minutes: string;
|
|
83
|
+
hour: string;
|
|
84
|
+
hours: string;
|
|
85
|
+
day: string;
|
|
86
|
+
days: string;
|
|
87
|
+
week: string;
|
|
88
|
+
weeks: string;
|
|
89
|
+
} | {
|
|
90
|
+
second: string;
|
|
91
|
+
seconds: string;
|
|
92
|
+
minute: string;
|
|
93
|
+
minutes: string;
|
|
94
|
+
hour: string;
|
|
95
|
+
hours: string;
|
|
96
|
+
day: string;
|
|
97
|
+
days: string;
|
|
98
|
+
week?: undefined;
|
|
99
|
+
weeks?: undefined;
|
|
100
|
+
};
|
|
101
|
+
export { indexer, modeIndexer, api, every, SENSITIVE_VALUE };
|