@superblocksteam/sdk 2.0.0-next.13 → 2.0.0-next.130
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/dist/application-build.d.mts +12 -0
- package/dist/application-build.d.mts.map +1 -0
- package/dist/application-build.mjs +113 -0
- package/dist/application-build.mjs.map +1 -0
- package/dist/cli-replacement/automatic-upgrades.d.ts.map +1 -1
- package/dist/cli-replacement/automatic-upgrades.js +19 -5
- package/dist/cli-replacement/automatic-upgrades.js.map +1 -1
- package/dist/cli-replacement/dev.d.mts.map +1 -1
- package/dist/cli-replacement/dev.mjs +7 -0
- package/dist/cli-replacement/dev.mjs.map +1 -1
- package/dist/client.d.ts +2 -1
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +7 -5
- package/dist/client.js.map +1 -1
- package/dist/dev-utils/dev-server.d.mts.map +1 -1
- package/dist/dev-utils/dev-server.mjs +30 -8
- package/dist/dev-utils/dev-server.mjs.map +1 -1
- package/dist/dev-utils/dev-tracer.d.ts.map +1 -1
- package/dist/dev-utils/dev-tracer.js +19 -0
- package/dist/dev-utils/dev-tracer.js.map +1 -1
- package/dist/dev-utils/vite-plugin-react-transform.d.mts.map +1 -1
- package/dist/dev-utils/vite-plugin-react-transform.mjs +1 -0
- package/dist/dev-utils/vite-plugin-react-transform.mjs.map +1 -1
- package/dist/dev-utils/vite-plugin-sb-cdn.d.mts.map +1 -1
- package/dist/dev-utils/vite-plugin-sb-cdn.mjs +28 -7
- package/dist/dev-utils/vite-plugin-sb-cdn.mjs.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/socket/handlers.d.ts +2 -2
- package/dist/socket/handlers.d.ts.map +1 -1
- package/dist/socket/handlers.js.map +1 -1
- package/dist/socket/index.d.ts +1 -11
- package/dist/socket/index.d.ts.map +1 -1
- package/dist/socket/index.js +11 -43
- package/dist/socket/index.js.map +1 -1
- package/dist/vite-plugin-inject-sb-ids-transform.d.mts +15 -0
- package/dist/vite-plugin-inject-sb-ids-transform.d.mts.map +1 -0
- package/dist/vite-plugin-inject-sb-ids-transform.mjs +86 -0
- package/dist/vite-plugin-inject-sb-ids-transform.mjs.map +1 -0
- package/package.json +16 -7
- package/src/application-build.mts +160 -0
- package/src/cli-replacement/automatic-upgrades.ts +22 -8
- package/src/cli-replacement/dev.mts +10 -0
- package/src/client.ts +13 -4
- package/src/dev-utils/dev-server.mts +39 -8
- package/src/dev-utils/dev-tracer.ts +18 -0
- package/src/dev-utils/vite-plugin-react-transform.mts +1 -0
- package/src/dev-utils/vite-plugin-sb-cdn.mts +35 -7
- package/src/index.ts +2 -0
- package/src/socket/handlers.ts +109 -105
- package/src/socket/index.ts +21 -101
- package/src/vite-plugin-inject-sb-ids-transform.mts +104 -0
- package/tsconfig.tsbuildinfo +1 -1
package/src/socket/handlers.ts
CHANGED
|
@@ -5,6 +5,7 @@ import type {
|
|
|
5
5
|
ApiToVerify,
|
|
6
6
|
ClientMethods,
|
|
7
7
|
ServerMethods,
|
|
8
|
+
RequestContextBase,
|
|
8
9
|
} from "@superblocksteam/shared";
|
|
9
10
|
import type { Signature } from "@superblocksteam/util";
|
|
10
11
|
|
|
@@ -29,18 +30,49 @@ export function createRequestHandlers({
|
|
|
29
30
|
token: string;
|
|
30
31
|
agentUrl?: string;
|
|
31
32
|
}) {
|
|
32
|
-
const requestHandlers: MethodHandlers<
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
33
|
+
const requestHandlers: MethodHandlers<
|
|
34
|
+
ClientMethods,
|
|
35
|
+
ServerMethods,
|
|
36
|
+
RequestContextBase
|
|
37
|
+
> = {
|
|
38
|
+
v1: {
|
|
39
|
+
signing: {
|
|
40
|
+
signApplication: [
|
|
41
|
+
async ({
|
|
42
|
+
branchName,
|
|
43
|
+
toSign,
|
|
44
|
+
}: {
|
|
45
|
+
branchName: string;
|
|
46
|
+
toSign: AppToSign;
|
|
47
|
+
}) => {
|
|
48
|
+
if (!agentUrl) {
|
|
49
|
+
throw new Error(
|
|
50
|
+
"Agent url not specified. This shouldn't happen.",
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
const signature = await signResource({
|
|
54
|
+
agentUrl,
|
|
55
|
+
token: token,
|
|
38
56
|
branchName,
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
57
|
+
resource: {
|
|
58
|
+
literal: {
|
|
59
|
+
data: toSign.rootHash,
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
});
|
|
63
|
+
return { signature: signature };
|
|
64
|
+
},
|
|
65
|
+
],
|
|
66
|
+
signApis: [
|
|
67
|
+
async ({
|
|
68
|
+
branchName,
|
|
69
|
+
toSign,
|
|
70
|
+
}: {
|
|
71
|
+
branchName: string;
|
|
72
|
+
toSign: ApiToSign[];
|
|
73
|
+
}) => {
|
|
74
|
+
const signatures: Signature[] = [];
|
|
75
|
+
for (const { apiPb } of toSign) {
|
|
44
76
|
if (!agentUrl) {
|
|
45
77
|
throw new Error(
|
|
46
78
|
"Agent url not specified. This shouldn't happen.",
|
|
@@ -50,105 +82,77 @@ export function createRequestHandlers({
|
|
|
50
82
|
agentUrl,
|
|
51
83
|
token: token,
|
|
52
84
|
branchName,
|
|
53
|
-
resource: {
|
|
54
|
-
literal: {
|
|
55
|
-
data: toSign.rootHash,
|
|
56
|
-
},
|
|
57
|
-
},
|
|
85
|
+
resource: { api: apiPb as any },
|
|
58
86
|
});
|
|
59
|
-
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
const signature = await signResource({
|
|
78
|
-
agentUrl,
|
|
79
|
-
token: token,
|
|
80
|
-
branchName,
|
|
81
|
-
resource: { api: apiPb as any },
|
|
82
|
-
});
|
|
83
|
-
signatures.push(signature);
|
|
87
|
+
signatures.push(signature);
|
|
88
|
+
}
|
|
89
|
+
return { signatures };
|
|
90
|
+
},
|
|
91
|
+
],
|
|
92
|
+
verifyApplication: [
|
|
93
|
+
async ({
|
|
94
|
+
branchName,
|
|
95
|
+
toVerify,
|
|
96
|
+
}: {
|
|
97
|
+
branchName: string;
|
|
98
|
+
toVerify: AppToVerify;
|
|
99
|
+
}) => {
|
|
100
|
+
try {
|
|
101
|
+
if (!agentUrl) {
|
|
102
|
+
throw new Error(
|
|
103
|
+
"Agent url not specified. This shouldn't happen.",
|
|
104
|
+
);
|
|
84
105
|
}
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
toVerify: AppToVerify;
|
|
95
|
-
}) => {
|
|
96
|
-
try {
|
|
97
|
-
if (!agentUrl) {
|
|
98
|
-
throw new Error(
|
|
99
|
-
"Agent url not specified. This shouldn't happen.",
|
|
100
|
-
);
|
|
101
|
-
}
|
|
102
|
-
await verifyResources({
|
|
103
|
-
agentUrl,
|
|
104
|
-
token,
|
|
105
|
-
branchName,
|
|
106
|
-
resources: [
|
|
107
|
-
{
|
|
108
|
-
literal: {
|
|
109
|
-
data: toVerify.rootHash,
|
|
110
|
-
signature: toVerify.signature,
|
|
111
|
-
},
|
|
106
|
+
await verifyResources({
|
|
107
|
+
agentUrl,
|
|
108
|
+
token,
|
|
109
|
+
branchName,
|
|
110
|
+
resources: [
|
|
111
|
+
{
|
|
112
|
+
literal: {
|
|
113
|
+
data: toVerify.rootHash,
|
|
114
|
+
signature: toVerify.signature,
|
|
112
115
|
},
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
|
|
116
|
+
},
|
|
117
|
+
],
|
|
118
|
+
});
|
|
119
|
+
return { ok: true };
|
|
120
|
+
} catch {
|
|
121
|
+
return { ok: false };
|
|
122
|
+
}
|
|
123
|
+
},
|
|
124
|
+
],
|
|
121
125
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
}
|
|
136
|
-
await verifyResources({
|
|
137
|
-
agentUrl,
|
|
138
|
-
token,
|
|
139
|
-
branchName,
|
|
140
|
-
resources: toVerify.map(({ apiPb }) => ({
|
|
141
|
-
api: apiPb as any,
|
|
142
|
-
})),
|
|
143
|
-
});
|
|
144
|
-
return { ok: true };
|
|
145
|
-
} catch {
|
|
146
|
-
return { ok: false };
|
|
126
|
+
verifyApi: [
|
|
127
|
+
async ({
|
|
128
|
+
branchName,
|
|
129
|
+
toVerify,
|
|
130
|
+
}: {
|
|
131
|
+
branchName: string;
|
|
132
|
+
toVerify: ApiToVerify[];
|
|
133
|
+
}) => {
|
|
134
|
+
try {
|
|
135
|
+
if (!agentUrl) {
|
|
136
|
+
throw new Error(
|
|
137
|
+
"Agent url not specified. This shouldn't happen.",
|
|
138
|
+
);
|
|
147
139
|
}
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
140
|
+
await verifyResources({
|
|
141
|
+
agentUrl,
|
|
142
|
+
token,
|
|
143
|
+
branchName,
|
|
144
|
+
resources: toVerify.map(({ apiPb }) => ({
|
|
145
|
+
api: apiPb as any,
|
|
146
|
+
})),
|
|
147
|
+
});
|
|
148
|
+
return { ok: true };
|
|
149
|
+
} catch {
|
|
150
|
+
return { ok: false };
|
|
151
|
+
}
|
|
152
|
+
},
|
|
153
|
+
],
|
|
151
154
|
},
|
|
152
|
-
}
|
|
155
|
+
},
|
|
156
|
+
};
|
|
153
157
|
return requestHandlers;
|
|
154
158
|
}
|
package/src/socket/index.ts
CHANGED
|
@@ -1,14 +1,16 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import {
|
|
2
|
+
connectWebSocket,
|
|
3
|
+
createISocketClient,
|
|
4
|
+
ISocketWithClientAuth,
|
|
5
|
+
} from "@superblocksteam/shared";
|
|
6
|
+
import tracer from "../dev-utils/dev-tracer.js";
|
|
3
7
|
import { createRequestHandlers } from "./handlers.js";
|
|
8
|
+
import type { ISocketClient } from "../types/index.js";
|
|
4
9
|
import type {
|
|
5
|
-
|
|
6
|
-
ISocketClient,
|
|
7
|
-
MethodHandlers,
|
|
10
|
+
ClientMethods,
|
|
8
11
|
RequestContextBase,
|
|
9
|
-
|
|
10
|
-
} from "
|
|
11
|
-
import type { ClientMethods, ServerMethods } from "@superblocksteam/shared";
|
|
12
|
+
ServerMethods,
|
|
13
|
+
} from "@superblocksteam/shared";
|
|
12
14
|
|
|
13
15
|
export type StdISocketRPCClient = ISocketClient<ServerMethods>;
|
|
14
16
|
|
|
@@ -39,101 +41,19 @@ export async function connectToISocketRPCServer({
|
|
|
39
41
|
wsUrl.hostname = "127.0.0.1";
|
|
40
42
|
}
|
|
41
43
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
44
|
+
const ws = await connectWebSocket(wsUrl.href);
|
|
45
|
+
const isocket = new ISocketWithClientAuth<
|
|
46
|
+
ClientMethods,
|
|
47
|
+
ServerMethods,
|
|
48
|
+
RequestContextBase
|
|
49
|
+
>(ws, authorization, requestHandlers, [], tracer, {
|
|
50
|
+
timeouts: {
|
|
48
51
|
connectionTimeoutInSeconds: 6 * 60, // 6 minutes
|
|
49
52
|
noResponseTimeoutInSeconds: 5 * 60, // 5 minutes
|
|
50
53
|
},
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
// a subclass of ISocket that sends an auth token on the first request
|
|
55
|
-
// this is useful for client-side sockets that need to authenticate
|
|
56
|
-
// TODO(george): if we start using this for long-lived connections, we should add a way to refresh the token
|
|
57
|
-
export class ISocketWithClientAuth<
|
|
58
|
-
ImplementedMethods,
|
|
59
|
-
CallableMethods,
|
|
60
|
-
RequestContext extends RequestContextBase,
|
|
61
|
-
> extends ISocket<ImplementedMethods, CallableMethods, RequestContext> {
|
|
62
|
-
private readonly authorization?: string;
|
|
63
|
-
private hasSentAuth = false;
|
|
64
|
-
|
|
65
|
-
constructor(
|
|
66
|
-
ws: WebSocket,
|
|
67
|
-
authorization: string | undefined,
|
|
68
|
-
requestHandlers: MethodHandlers<
|
|
69
|
-
ImplementedMethods,
|
|
70
|
-
CallableMethods,
|
|
71
|
-
RequestContext
|
|
72
|
-
>,
|
|
73
|
-
globalMiddlewares: GenericMiddleware<CallableMethods, RequestContext>[],
|
|
74
|
-
timeouts?: SocketTimeouts,
|
|
75
|
-
) {
|
|
76
|
-
super(ws, requestHandlers, globalMiddlewares, timeouts);
|
|
77
|
-
this.authorization = authorization;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
// override `request` from the base class to send `authorization` when appropriate
|
|
81
|
-
async request<Params, Result>(
|
|
82
|
-
method: string,
|
|
83
|
-
params: Params,
|
|
84
|
-
): Promise<Result> {
|
|
85
|
-
// only send `authorization` on the first request
|
|
86
|
-
const authorization = this.hasSentAuth ? undefined : this.authorization;
|
|
87
|
-
const result = await super.request<Params, Result>(
|
|
88
|
-
method,
|
|
89
|
-
params,
|
|
90
|
-
authorization,
|
|
91
|
-
);
|
|
92
|
-
this.hasSentAuth = true;
|
|
93
|
-
return result;
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
export async function connectISocket<
|
|
98
|
-
CallableMethods,
|
|
99
|
-
ImplementedMethods,
|
|
100
|
-
RequestContext extends RequestContextBase = RequestContextBase,
|
|
101
|
-
>(
|
|
102
|
-
wsUrl: string,
|
|
103
|
-
authorization: string | undefined,
|
|
104
|
-
requestHandlers: MethodHandlers<
|
|
105
|
-
ImplementedMethods,
|
|
106
|
-
CallableMethods,
|
|
107
|
-
RequestContext
|
|
108
|
-
>,
|
|
109
|
-
globalMiddlewares: GenericMiddleware<CallableMethods, RequestContext>[],
|
|
110
|
-
timeouts?: SocketTimeouts,
|
|
111
|
-
): Promise<ISocketClient<CallableMethods>> {
|
|
112
|
-
const ws = await connectWebSocket(wsUrl);
|
|
113
|
-
const isocket = new ISocketWithClientAuth(
|
|
114
|
-
ws,
|
|
115
|
-
authorization,
|
|
116
|
-
requestHandlers,
|
|
117
|
-
globalMiddlewares,
|
|
118
|
-
timeouts,
|
|
119
|
-
);
|
|
120
|
-
return createISocketClient(isocket);
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
export function connectWebSocket(wsUrl: string): Promise<WebSocket> {
|
|
124
|
-
return new Promise((resolve, reject) => {
|
|
125
|
-
const ws = new WebSocket(wsUrl);
|
|
126
|
-
|
|
127
|
-
ws.addEventListener("open", () => {
|
|
128
|
-
// Resolve the promise with the WebSocket instance when the connection is open
|
|
129
|
-
resolve(ws);
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
133
|
-
// @ts-ignore
|
|
134
|
-
ws.addEventListener("error", (error: Error) => {
|
|
135
|
-
// Reject the promise if there's an error
|
|
136
|
-
reject(error);
|
|
137
|
-
});
|
|
54
|
+
onClose: () => {
|
|
55
|
+
// Noop
|
|
56
|
+
},
|
|
138
57
|
});
|
|
58
|
+
return createISocketClient(isocket);
|
|
139
59
|
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import babelGenerate from "@babel/generator";
|
|
3
|
+
import { parse } from "@babel/parser";
|
|
4
|
+
import {
|
|
5
|
+
supplementElementIds,
|
|
6
|
+
generateRootSource,
|
|
7
|
+
} from "@superblocksteam/vite-plugin-file-sync";
|
|
8
|
+
import { yellow, red } from "colorette";
|
|
9
|
+
import fs from "fs-extra";
|
|
10
|
+
import { createLogger } from "vite";
|
|
11
|
+
import { getLogger } from "./dev-utils/dev-logger.mjs";
|
|
12
|
+
import type { HydratedRoute } from "@superblocksteam/vite-plugin-file-sync";
|
|
13
|
+
import type { Logger, Plugin } from "vite";
|
|
14
|
+
|
|
15
|
+
const routesFileBaseName = "routes.json";
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Creates a Vite plugin that injects Superblocks IDs into the application.
|
|
19
|
+
* This will primarily be used during builds, as the dev server leverages the
|
|
20
|
+
* file sync manager to inject and keep the IDs up to date.
|
|
21
|
+
*
|
|
22
|
+
* Features:
|
|
23
|
+
* - Injects the root component with the routes data
|
|
24
|
+
* - Injects Superblocks IDs into all components
|
|
25
|
+
*
|
|
26
|
+
* @param root - The root directory of the application
|
|
27
|
+
* @returns A Vite plugin that injects Superblocks IDs into the application's components
|
|
28
|
+
*/
|
|
29
|
+
export async function injectSuperblocksIdsPlugin(root: string) {
|
|
30
|
+
const viteLogger = createLogger();
|
|
31
|
+
const logger = getLogger();
|
|
32
|
+
viteLogger.info = logger.info;
|
|
33
|
+
viteLogger.warn = (msg: string) => {
|
|
34
|
+
logger.warn(yellow(msg));
|
|
35
|
+
};
|
|
36
|
+
viteLogger.warnOnce = (msg: string) => {
|
|
37
|
+
logger.warn(yellow(msg));
|
|
38
|
+
};
|
|
39
|
+
viteLogger.error = (msg: string) => {
|
|
40
|
+
logger.error(red(msg));
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
viteLogger.clearScreen = () => {};
|
|
44
|
+
|
|
45
|
+
const routes: HydratedRoute[] = await getRoutes(root, viteLogger);
|
|
46
|
+
|
|
47
|
+
return {
|
|
48
|
+
name: "sb-inject-superblocks-ids",
|
|
49
|
+
enforce: "pre",
|
|
50
|
+
|
|
51
|
+
transform(code, id) {
|
|
52
|
+
const relativePath = path.relative(root, id);
|
|
53
|
+
|
|
54
|
+
if (relativePath === "root.tsx") {
|
|
55
|
+
const source = generateRootSource(code, routes);
|
|
56
|
+
return {
|
|
57
|
+
code: source,
|
|
58
|
+
map: null,
|
|
59
|
+
};
|
|
60
|
+
} else if (id.endsWith(".tsx")) {
|
|
61
|
+
const ast = parse(code, {
|
|
62
|
+
sourceType: "module",
|
|
63
|
+
sourceFilename: id,
|
|
64
|
+
plugins: ["jsx"],
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
supplementElementIds({
|
|
68
|
+
fileName: id,
|
|
69
|
+
ast,
|
|
70
|
+
shouldModifyAst: true,
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
const result = babelGenerate.default(ast);
|
|
74
|
+
return result.code;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return code;
|
|
78
|
+
},
|
|
79
|
+
} as Plugin;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
async function getRoutes(root: string, logger: Logger) {
|
|
83
|
+
const routesFile = path.join(root, routesFileBaseName);
|
|
84
|
+
if (!(await fs.pathExists(routesFile))) {
|
|
85
|
+
logger.warn(`routes file not found at expected location: ${routesFile}`);
|
|
86
|
+
return [];
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
try {
|
|
90
|
+
const routesData = await fs.readFile(routesFile, "utf-8");
|
|
91
|
+
const routes = JSON.parse(routesData) as Record<string, { file: string }>;
|
|
92
|
+
|
|
93
|
+
return Object.entries(routes).map(([path, { file }]) => ({
|
|
94
|
+
path,
|
|
95
|
+
component: file,
|
|
96
|
+
}));
|
|
97
|
+
} catch (err) {
|
|
98
|
+
logger.error(
|
|
99
|
+
`error reading routes file: ${routesFile}. error=[${JSON.stringify(err)}]`,
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return [];
|
|
104
|
+
}
|