@valon-technologies/gestalt 0.0.1-alpha.1
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/README.md +160 -0
- package/gen/v1/auth_pb.ts +212 -0
- package/gen/v1/cache_pb.ts +357 -0
- package/gen/v1/datastore_pb.ts +922 -0
- package/gen/v1/plugin_pb.ts +772 -0
- package/gen/v1/runtime_pb.ts +216 -0
- package/gen/v1/s3_pb.ts +640 -0
- package/gen/v1/secrets_pb.ts +63 -0
- package/package.json +55 -0
- package/src/api.ts +98 -0
- package/src/auth.ts +103 -0
- package/src/build.ts +181 -0
- package/src/cache.ts +304 -0
- package/src/catalog.ts +188 -0
- package/src/index.ts +182 -0
- package/src/indexeddb.ts +740 -0
- package/src/plugin.ts +402 -0
- package/src/provider.ts +133 -0
- package/src/runtime.ts +871 -0
- package/src/s3.ts +1128 -0
- package/src/schema.ts +219 -0
- package/src/secrets.ts +36 -0
- package/src/target.ts +192 -0
- package/tsconfig.json +22 -0
package/package.json
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@valon-technologies/gestalt",
|
|
3
|
+
"version": "0.0.1-alpha.1",
|
|
4
|
+
"description": "TypeScript SDK for Gestalt executable providers",
|
|
5
|
+
"license": "Apache-2.0",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "https://github.com/valon-technologies/gestalt",
|
|
10
|
+
"directory": "sdk/typescript"
|
|
11
|
+
},
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/valon-technologies/gestalt/issues"
|
|
14
|
+
},
|
|
15
|
+
"homepage": "https://github.com/valon-technologies/gestalt/tree/main/sdk/typescript#readme",
|
|
16
|
+
"exports": {
|
|
17
|
+
".": "./src/index.ts",
|
|
18
|
+
"./build": "./src/build.ts",
|
|
19
|
+
"./runtime": "./src/runtime.ts",
|
|
20
|
+
"./schema": "./src/schema.ts",
|
|
21
|
+
"./target": "./src/target.ts"
|
|
22
|
+
},
|
|
23
|
+
"bin": {
|
|
24
|
+
"gestalt-ts-build": "./src/build.ts",
|
|
25
|
+
"gestalt-ts-runtime": "./src/runtime.ts"
|
|
26
|
+
},
|
|
27
|
+
"files": [
|
|
28
|
+
"src",
|
|
29
|
+
"gen",
|
|
30
|
+
"README.md",
|
|
31
|
+
"tsconfig.json"
|
|
32
|
+
],
|
|
33
|
+
"publishConfig": {
|
|
34
|
+
"access": "public"
|
|
35
|
+
},
|
|
36
|
+
"scripts": {
|
|
37
|
+
"build:proto": "buf generate --template ../proto/buf.typescript.gen.yaml ../proto",
|
|
38
|
+
"typecheck": "tsc --noEmit",
|
|
39
|
+
"test": "bun test",
|
|
40
|
+
"check": "bun test && tsc --noEmit"
|
|
41
|
+
},
|
|
42
|
+
"dependencies": {
|
|
43
|
+
"@bufbuild/protobuf": "2.11.0",
|
|
44
|
+
"@connectrpc/connect": "2.1.0",
|
|
45
|
+
"@connectrpc/connect-node": "2.1.0",
|
|
46
|
+
"yaml": "2.8.1"
|
|
47
|
+
},
|
|
48
|
+
"devDependencies": {
|
|
49
|
+
"@types/bun": "1.3.2",
|
|
50
|
+
"typescript": "5.9.3"
|
|
51
|
+
},
|
|
52
|
+
"peerDependencies": {
|
|
53
|
+
"typescript": "^5.9.0"
|
|
54
|
+
}
|
|
55
|
+
}
|
package/src/api.ts
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
export interface Subject {
|
|
2
|
+
id: string;
|
|
3
|
+
kind: string;
|
|
4
|
+
displayName: string;
|
|
5
|
+
authSource: string;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export interface Credential {
|
|
9
|
+
mode: string;
|
|
10
|
+
subjectId: string;
|
|
11
|
+
connection: string;
|
|
12
|
+
instance: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface Access {
|
|
16
|
+
policy: string;
|
|
17
|
+
role: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface Request {
|
|
21
|
+
token: string;
|
|
22
|
+
connectionParams: Record<string, string>;
|
|
23
|
+
subject: Subject;
|
|
24
|
+
credential: Credential;
|
|
25
|
+
access: Access;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export const responseBrand: unique symbol = Symbol("gestalt.response");
|
|
29
|
+
|
|
30
|
+
export interface Response<T> {
|
|
31
|
+
readonly [responseBrand]: true;
|
|
32
|
+
status?: number;
|
|
33
|
+
body: T;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface OperationResult {
|
|
37
|
+
status: number;
|
|
38
|
+
body: string;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export type MaybePromise<T> = T | Promise<T>;
|
|
42
|
+
|
|
43
|
+
export function response<T>(status: number, body: T): Response<T> {
|
|
44
|
+
return {
|
|
45
|
+
[responseBrand]: true,
|
|
46
|
+
status,
|
|
47
|
+
body,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function ok<T>(body: T): Response<T> {
|
|
52
|
+
return response(200, body);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function request(
|
|
56
|
+
token = "",
|
|
57
|
+
connectionParams: Record<string, string> = {},
|
|
58
|
+
subject: Partial<Subject> = {},
|
|
59
|
+
credential: Partial<Credential> = {},
|
|
60
|
+
access: Partial<Access> = {},
|
|
61
|
+
): Request {
|
|
62
|
+
return {
|
|
63
|
+
token,
|
|
64
|
+
connectionParams: {
|
|
65
|
+
...connectionParams,
|
|
66
|
+
},
|
|
67
|
+
subject: {
|
|
68
|
+
id: subject.id ?? "",
|
|
69
|
+
kind: subject.kind ?? "",
|
|
70
|
+
displayName: subject.displayName ?? "",
|
|
71
|
+
authSource: subject.authSource ?? "",
|
|
72
|
+
},
|
|
73
|
+
credential: {
|
|
74
|
+
mode: credential.mode ?? "",
|
|
75
|
+
subjectId: credential.subjectId ?? "",
|
|
76
|
+
connection: credential.connection ?? "",
|
|
77
|
+
instance: credential.instance ?? "",
|
|
78
|
+
},
|
|
79
|
+
access: {
|
|
80
|
+
policy: access.policy ?? "",
|
|
81
|
+
role: access.role ?? "",
|
|
82
|
+
},
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export function connectionParam(
|
|
87
|
+
input: Request | undefined,
|
|
88
|
+
name: string,
|
|
89
|
+
): string | undefined {
|
|
90
|
+
return input?.connectionParams[name];
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export function errorMessage(error: unknown): string {
|
|
94
|
+
if (error instanceof Error && error.message) {
|
|
95
|
+
return error.message;
|
|
96
|
+
}
|
|
97
|
+
return String(error);
|
|
98
|
+
}
|
package/src/auth.ts
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { RuntimeProvider, type RuntimeProviderOptions } from "./provider.ts";
|
|
2
|
+
import type { MaybePromise } from "./api.ts";
|
|
3
|
+
|
|
4
|
+
export interface AuthenticatedUser {
|
|
5
|
+
subject: string;
|
|
6
|
+
email?: string;
|
|
7
|
+
emailVerified?: boolean;
|
|
8
|
+
displayName?: string;
|
|
9
|
+
avatarUrl?: string;
|
|
10
|
+
claims?: Record<string, string>;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface BeginLoginRequest {
|
|
14
|
+
callbackUrl: string;
|
|
15
|
+
hostState: string;
|
|
16
|
+
scopes: string[];
|
|
17
|
+
options: Record<string, string>;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface BeginLoginResponse {
|
|
21
|
+
authorizationUrl: string;
|
|
22
|
+
providerState?: Uint8Array;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface CompleteLoginRequest {
|
|
26
|
+
query: Record<string, string>;
|
|
27
|
+
providerState: Uint8Array;
|
|
28
|
+
callbackUrl: string;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface AuthSessionSettings {
|
|
32
|
+
sessionTtlSeconds: number | bigint;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface AuthProviderOptions extends RuntimeProviderOptions {
|
|
36
|
+
beginLogin: (
|
|
37
|
+
request: BeginLoginRequest,
|
|
38
|
+
) => MaybePromise<BeginLoginResponse>;
|
|
39
|
+
completeLogin: (
|
|
40
|
+
request: CompleteLoginRequest,
|
|
41
|
+
) => MaybePromise<AuthenticatedUser>;
|
|
42
|
+
validateExternalToken?: (
|
|
43
|
+
token: string,
|
|
44
|
+
) => MaybePromise<AuthenticatedUser | null | undefined>;
|
|
45
|
+
sessionSettings?: () => MaybePromise<AuthSessionSettings>;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export class AuthProvider extends RuntimeProvider {
|
|
49
|
+
readonly kind = "auth" as const;
|
|
50
|
+
|
|
51
|
+
private readonly beginLoginHandler: AuthProviderOptions["beginLogin"];
|
|
52
|
+
private readonly completeLoginHandler: AuthProviderOptions["completeLogin"];
|
|
53
|
+
private readonly validateExternalTokenHandler: AuthProviderOptions["validateExternalToken"];
|
|
54
|
+
private readonly sessionSettingsHandler: AuthProviderOptions["sessionSettings"];
|
|
55
|
+
|
|
56
|
+
constructor(options: AuthProviderOptions) {
|
|
57
|
+
super(options);
|
|
58
|
+
this.beginLoginHandler = options.beginLogin;
|
|
59
|
+
this.completeLoginHandler = options.completeLogin;
|
|
60
|
+
this.validateExternalTokenHandler = options.validateExternalToken;
|
|
61
|
+
this.sessionSettingsHandler = options.sessionSettings;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
async beginLogin(request: BeginLoginRequest): Promise<BeginLoginResponse> {
|
|
65
|
+
return await this.beginLoginHandler(request);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
async completeLogin(request: CompleteLoginRequest): Promise<AuthenticatedUser> {
|
|
69
|
+
return await this.completeLoginHandler(request);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
supportsExternalTokenValidation(): boolean {
|
|
73
|
+
return this.validateExternalTokenHandler !== undefined;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
async validateExternalToken(token: string): Promise<AuthenticatedUser | null | undefined> {
|
|
77
|
+
return await this.validateExternalTokenHandler?.(token);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
supportsSessionSettings(): boolean {
|
|
81
|
+
return this.sessionSettingsHandler !== undefined;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
async sessionSettings(): Promise<AuthSessionSettings | undefined> {
|
|
85
|
+
return await this.sessionSettingsHandler?.();
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export function defineAuthProvider(options: AuthProviderOptions): AuthProvider {
|
|
90
|
+
return new AuthProvider(options);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export function isAuthProvider(value: unknown): value is AuthProvider {
|
|
94
|
+
return (
|
|
95
|
+
value instanceof AuthProvider ||
|
|
96
|
+
(typeof value === "object" &&
|
|
97
|
+
value !== null &&
|
|
98
|
+
"kind" in value &&
|
|
99
|
+
(value as { kind?: unknown }).kind === "auth" &&
|
|
100
|
+
"beginLogin" in value &&
|
|
101
|
+
"completeLogin" in value)
|
|
102
|
+
);
|
|
103
|
+
}
|
package/src/build.ts
ADDED
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import { spawnSync } from "node:child_process";
|
|
2
|
+
import { existsSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { homedir, tmpdir } from "node:os";
|
|
4
|
+
import { join, resolve } from "node:path";
|
|
5
|
+
|
|
6
|
+
import { parseProviderTarget, resolveProviderModulePath, type ProviderTarget } from "./target.ts";
|
|
7
|
+
|
|
8
|
+
export const USAGE =
|
|
9
|
+
"usage: bun run build.ts ROOT PROVIDER_TARGET OUTPUT PROVIDER_NAME GOOS GOARCH";
|
|
10
|
+
|
|
11
|
+
export type BuildArgs = {
|
|
12
|
+
root: string;
|
|
13
|
+
target: string;
|
|
14
|
+
outputPath: string;
|
|
15
|
+
providerName: string;
|
|
16
|
+
goos: string;
|
|
17
|
+
goarch: string;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export async function main(argv: string[] = process.argv.slice(2)): Promise<number> {
|
|
21
|
+
const args = parseBuildArgs(argv);
|
|
22
|
+
if (!args) {
|
|
23
|
+
console.error(USAGE);
|
|
24
|
+
return 2;
|
|
25
|
+
}
|
|
26
|
+
buildProviderBinary(args);
|
|
27
|
+
return 0;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function parseBuildArgs(argv: string[]): BuildArgs | undefined {
|
|
31
|
+
if (argv.length !== 6) {
|
|
32
|
+
return undefined;
|
|
33
|
+
}
|
|
34
|
+
return {
|
|
35
|
+
root: argv[0]!,
|
|
36
|
+
target: argv[1]!,
|
|
37
|
+
outputPath: argv[2]!,
|
|
38
|
+
providerName: argv[3]!,
|
|
39
|
+
goos: argv[4]!,
|
|
40
|
+
goarch: argv[5]!,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function buildProviderBinary(args: BuildArgs): void {
|
|
45
|
+
const root = resolve(args.root);
|
|
46
|
+
const outputPath = resolve(args.outputPath);
|
|
47
|
+
const target = parseProviderTarget(args.target);
|
|
48
|
+
const workDir = mkdtempSync(join(tmpdir(), "gestalt-typescript-build-"));
|
|
49
|
+
|
|
50
|
+
try {
|
|
51
|
+
const wrapperPath = writeBundledWrapper(workDir, root, target, args.providerName);
|
|
52
|
+
const bunCommand = bunBuildCommand(wrapperPath, outputPath, args.goos, args.goarch);
|
|
53
|
+
const result = spawnSync(bunCommand.command, bunCommand.args, {
|
|
54
|
+
cwd: root,
|
|
55
|
+
stdio: "inherit",
|
|
56
|
+
});
|
|
57
|
+
if (result.status !== 0) {
|
|
58
|
+
throw new Error(`bun build failed with status ${result.status ?? "unknown"}`);
|
|
59
|
+
}
|
|
60
|
+
} finally {
|
|
61
|
+
rmSync(workDir, {
|
|
62
|
+
recursive: true,
|
|
63
|
+
force: true,
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export const buildPluginBinary = buildProviderBinary;
|
|
69
|
+
|
|
70
|
+
export function bunBuildCommand(
|
|
71
|
+
wrapperPath: string,
|
|
72
|
+
outputPath: string,
|
|
73
|
+
goos: string,
|
|
74
|
+
goarch: string,
|
|
75
|
+
): { command: string; args: string[] } {
|
|
76
|
+
return {
|
|
77
|
+
command: resolveBunExecutable(),
|
|
78
|
+
args: [
|
|
79
|
+
"build",
|
|
80
|
+
"--compile",
|
|
81
|
+
"--target",
|
|
82
|
+
bunTarget(goos, goarch),
|
|
83
|
+
"--outfile",
|
|
84
|
+
outputPath,
|
|
85
|
+
wrapperPath,
|
|
86
|
+
],
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export function bunTarget(goos: string, goarch: string): string {
|
|
91
|
+
const key = `${goos}/${goarch}`;
|
|
92
|
+
switch (key) {
|
|
93
|
+
case "darwin/amd64":
|
|
94
|
+
return "bun-darwin-x64";
|
|
95
|
+
case "darwin/arm64":
|
|
96
|
+
return "bun-darwin-arm64";
|
|
97
|
+
case "linux/amd64":
|
|
98
|
+
return "bun-linux-x64";
|
|
99
|
+
case "linux/arm64":
|
|
100
|
+
return "bun-linux-arm64";
|
|
101
|
+
case "windows/amd64":
|
|
102
|
+
return "bun-windows-x64";
|
|
103
|
+
case "windows/arm64":
|
|
104
|
+
return "bun-windows-arm64";
|
|
105
|
+
default:
|
|
106
|
+
throw new Error(`unsupported Bun target for ${key}`);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function writeBundledWrapper(
|
|
111
|
+
workDir: string,
|
|
112
|
+
root: string,
|
|
113
|
+
target: ProviderTarget,
|
|
114
|
+
providerName: string,
|
|
115
|
+
): string {
|
|
116
|
+
const wrapperPath = join(workDir, "bundled-runtime.ts");
|
|
117
|
+
const modulePath = JSON.stringify(resolveProviderModulePath(root, target));
|
|
118
|
+
const runtimePath = JSON.stringify(resolve(import.meta.dir, "runtime.ts"));
|
|
119
|
+
const exportName = target.exportName ? JSON.stringify(target.exportName) : "undefined";
|
|
120
|
+
const source = `
|
|
121
|
+
import * as bundledModule from ${modulePath};
|
|
122
|
+
import { runBundledProvider } from ${runtimePath};
|
|
123
|
+
|
|
124
|
+
const candidate = ${
|
|
125
|
+
target.exportName
|
|
126
|
+
? `bundledModule[${exportName}]`
|
|
127
|
+
: defaultBundledCandidateExpression(target.kind)
|
|
128
|
+
};
|
|
129
|
+
await runBundledProvider(candidate, ${JSON.stringify(target.kind)}, ${JSON.stringify(providerName)});
|
|
130
|
+
`;
|
|
131
|
+
writeFileSync(wrapperPath, source, "utf8");
|
|
132
|
+
return wrapperPath;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function defaultBundledCandidateExpression(kind: ProviderTarget["kind"]): string {
|
|
136
|
+
switch (kind) {
|
|
137
|
+
case "integration":
|
|
138
|
+
return "bundledModule.provider ?? bundledModule.plugin ?? bundledModule.default";
|
|
139
|
+
case "auth":
|
|
140
|
+
return "bundledModule.auth ?? bundledModule.provider ?? bundledModule.default";
|
|
141
|
+
case "cache":
|
|
142
|
+
return "bundledModule.cache ?? bundledModule.provider ?? bundledModule.default";
|
|
143
|
+
case "secrets":
|
|
144
|
+
return "bundledModule.secrets ?? bundledModule.provider ?? bundledModule.default";
|
|
145
|
+
case "s3":
|
|
146
|
+
return "bundledModule.s3 ?? bundledModule.provider ?? bundledModule.default";
|
|
147
|
+
case "telemetry":
|
|
148
|
+
return "bundledModule.telemetry ?? bundledModule.provider ?? bundledModule.default";
|
|
149
|
+
}
|
|
150
|
+
throw new Error(`unsupported provider kind: ${kind satisfies never}`);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function resolveBunExecutable(): string {
|
|
154
|
+
const candidates = [
|
|
155
|
+
process.env.GESTALT_BUN,
|
|
156
|
+
join(homedir(), ".bun", "bin", "bun"),
|
|
157
|
+
"bun",
|
|
158
|
+
].filter((value): value is string => Boolean(value));
|
|
159
|
+
|
|
160
|
+
for (const candidate of candidates) {
|
|
161
|
+
if (candidate === "bun") {
|
|
162
|
+
return candidate;
|
|
163
|
+
}
|
|
164
|
+
if (existsSync(candidate)) {
|
|
165
|
+
return candidate;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
return "bun";
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
if (import.meta.main) {
|
|
172
|
+
void main().then(
|
|
173
|
+
(code) => {
|
|
174
|
+
process.exitCode = code;
|
|
175
|
+
},
|
|
176
|
+
(error: unknown) => {
|
|
177
|
+
console.error(error instanceof Error ? error.stack ?? error.message : String(error));
|
|
178
|
+
process.exitCode = 1;
|
|
179
|
+
},
|
|
180
|
+
);
|
|
181
|
+
}
|