@valon-technologies/gestalt 0.0.1-alpha.8 → 0.0.1-alpha.9

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/src/target.ts CHANGED
@@ -2,39 +2,39 @@ import { readFileSync } from "node:fs";
2
2
  import { isAbsolute, normalize, resolve } from "node:path";
3
3
  import { pathToFileURL } from "node:url";
4
4
 
5
+ import {
6
+ formatExternalProviderKind,
7
+ isExternalProviderKindToken,
8
+ parseExternalProviderKind,
9
+ } from "./provider-kind.ts";
5
10
  import { slugName, type ProviderKind } from "./provider.ts";
6
11
 
12
+ /**
13
+ * Relative module target with an optional named export.
14
+ */
7
15
  export type ModuleTarget = {
8
16
  modulePath: string;
9
17
  exportName?: string;
10
18
  };
11
19
 
20
+ /**
21
+ * Provider target with an explicit Gestalt provider kind.
22
+ */
12
23
  export type ProviderTarget = ModuleTarget & {
13
24
  kind: ProviderKind;
14
25
  };
15
26
 
27
+ /**
28
+ * Gestalt-specific package metadata read from `package.json`.
29
+ */
16
30
  export type PackageConfig = {
17
31
  name?: string;
18
32
  providerTarget?: ProviderTarget;
19
33
  };
20
34
 
21
- type PackageProviderConfig =
22
- | string
23
- | {
24
- kind?: string;
25
- target?: string;
26
- };
27
-
28
- const EXTERNAL_PROVIDER_KIND_TOKENS = new Set<string>([
29
- "plugin",
30
- "integration",
31
- "auth",
32
- "cache",
33
- "secrets",
34
- "s3",
35
- "telemetry",
36
- ]);
37
-
35
+ /**
36
+ * Parses a relative module target in the form `./file.ts#namedExport`.
37
+ */
38
38
  export function parseModuleTarget(target: string, label = "gestalt provider target"): ModuleTarget {
39
39
  const [modulePathRaw, exportNameRaw] = target.split("#", 2);
40
40
  const modulePath = modulePathRaw?.trim() ?? "";
@@ -59,7 +59,12 @@ export function parseModuleTarget(target: string, label = "gestalt provider targ
59
59
  return parsed;
60
60
  }
61
61
 
62
- export function parseProviderTarget(target: string | PackageProviderConfig): ProviderTarget {
62
+ /**
63
+ * Parses either a string or object-form provider target from `package.json`.
64
+ */
65
+ export function parseProviderTarget(
66
+ target: string | { kind?: string; target?: string },
67
+ ): ProviderTarget {
63
68
  if (typeof target === "string") {
64
69
  const prefixed = parseKindPrefixedTarget(target);
65
70
  if (prefixed) {
@@ -71,7 +76,7 @@ export function parseProviderTarget(target: string | PackageProviderConfig): Pro
71
76
  };
72
77
  }
73
78
 
74
- const kind = parseProviderKind(target.kind ?? "integration");
79
+ const kind = parseExternalProviderKind(target.kind ?? "plugin");
75
80
  if (!target.target || typeof target.target !== "string") {
76
81
  throw new Error("gestalt.provider.target is required");
77
82
  }
@@ -81,8 +86,9 @@ export function parseProviderTarget(target: string | PackageProviderConfig): Pro
81
86
  };
82
87
  }
83
88
 
84
- export const parsePluginTarget = parseModuleTarget;
85
-
89
+ /**
90
+ * Reads the Gestalt-specific provider metadata from a package directory.
91
+ */
86
92
  export function readPackageConfig(root: string): PackageConfig {
87
93
  const packagePath = resolve(root, "package.json");
88
94
  const raw = JSON.parse(readFileSync(packagePath, "utf8")) as Record<string, unknown>;
@@ -94,11 +100,6 @@ export function readPackageConfig(root: string): PackageConfig {
94
100
  providerTarget = parseProviderTarget(gestalt.provider);
95
101
  } else if (isProviderConfigObject(gestalt.provider)) {
96
102
  providerTarget = parseProviderTarget(gestalt.provider);
97
- } else if (typeof gestalt.plugin === "string") {
98
- providerTarget = {
99
- kind: "integration",
100
- ...parseModuleTarget(gestalt.plugin, "gestalt.plugin"),
101
- };
102
103
  }
103
104
 
104
105
  const config: PackageConfig = {};
@@ -111,29 +112,28 @@ export function readPackageConfig(root: string): PackageConfig {
111
112
  return config;
112
113
  }
113
114
 
115
+ /**
116
+ * Reads and validates the configured provider target from `package.json`.
117
+ */
114
118
  export function readPackageProviderTarget(root: string): ProviderTarget {
115
119
  const config = readPackageConfig(root);
116
120
  if (!config.providerTarget) {
117
- throw new Error("package.json gestalt.provider or gestalt.plugin is required");
121
+ throw new Error("package.json gestalt.provider is required");
118
122
  }
119
123
  return config.providerTarget;
120
124
  }
121
125
 
122
- export function readPackagePluginTarget(root: string): string {
123
- const target = readPackageProviderTarget(root);
124
- if (target.kind !== "integration") {
125
- throw new Error(`package.json provider kind ${JSON.stringify(target.kind)} is not an integration provider`);
126
- }
127
- return formatModuleTarget(target);
128
- }
129
-
126
+ /**
127
+ * Computes a default provider slug from the package name.
128
+ */
130
129
  export function defaultProviderName(root: string): string {
131
130
  const config = readPackageConfig(root);
132
131
  return slugName(config.name ?? "");
133
132
  }
134
133
 
135
- export const defaultPluginName = defaultProviderName;
136
-
134
+ /**
135
+ * Resolves a provider target to an absolute file path.
136
+ */
137
137
  export function resolveProviderModulePath(root: string, target: ProviderTarget | ModuleTarget): string {
138
138
  const absolute = resolve(root, target.modulePath);
139
139
  if (!isAbsolute(absolute)) {
@@ -142,49 +142,47 @@ export function resolveProviderModulePath(root: string, target: ProviderTarget |
142
142
  return normalize(absolute);
143
143
  }
144
144
 
145
- export const resolvePluginModulePath = resolveProviderModulePath;
146
-
145
+ /**
146
+ * Resolves a provider target to an importable file URL.
147
+ */
147
148
  export function resolveProviderImportUrl(root: string, target: ProviderTarget | ModuleTarget): string {
148
149
  return pathToFileURL(resolveProviderModulePath(root, target)).href;
149
150
  }
150
151
 
151
- export const resolvePluginImportUrl = resolveProviderImportUrl;
152
-
152
+ /**
153
+ * Formats a provider target using the public `kind:./path#export` syntax.
154
+ */
153
155
  export function formatProviderTarget(target: ProviderTarget): string {
154
156
  return `${formatProviderKind(target.kind)}:${formatModuleTarget(target)}`;
155
157
  }
156
158
 
159
+ /**
160
+ * Formats a module target using the public `./path#export` syntax.
161
+ */
157
162
  export function formatModuleTarget(target: ModuleTarget): string {
158
163
  return `${target.modulePath}${target.exportName ? `#${target.exportName}` : ""}`;
159
164
  }
160
165
 
161
166
  function parseKindPrefixedTarget(target: string): ProviderTarget | undefined {
162
- const match = target.match(/^(plugin|integration|auth|cache|secrets|s3|telemetry):(.*)$/);
163
- if (!match) {
167
+ const separator = target.indexOf(":");
168
+ if (separator < 0) {
169
+ return undefined;
170
+ }
171
+ const kindToken = target.slice(0, separator);
172
+ if (!kindToken || !isExternalProviderKindToken(kindToken)) {
173
+ if (kindToken && !kindToken.startsWith(".") && !kindToken.startsWith("/")) {
174
+ parseExternalProviderKind(kindToken);
175
+ }
164
176
  return undefined;
165
177
  }
166
178
  return {
167
- kind: parseProviderKind(match[1]!),
168
- ...parseModuleTarget(match[2]!, "provider target"),
179
+ kind: parseExternalProviderKind(kindToken),
180
+ ...parseModuleTarget(target.slice(separator + 1), "provider target"),
169
181
  };
170
182
  }
171
183
 
172
- function parseProviderKind(value: string): ProviderKind {
173
- const normalized = value.trim().toLowerCase();
174
- if (!EXTERNAL_PROVIDER_KIND_TOKENS.has(normalized)) {
175
- throw new Error(`unsupported provider kind ${JSON.stringify(value)}`);
176
- }
177
- if (normalized === "plugin") {
178
- return "integration";
179
- }
180
- return normalized as ProviderKind;
181
- }
182
-
183
184
  function formatProviderKind(kind: ProviderKind): string {
184
- if (kind === "integration") {
185
- return "plugin";
186
- }
187
- return kind;
185
+ return formatExternalProviderKind(kind);
188
186
  }
189
187
 
190
188
  function isProviderConfigObject(value: unknown): value is { kind?: string; target?: string } {
@@ -0,0 +1,131 @@
1
+ import { connect } from "node:net";
2
+
3
+ import type { MessageInitShape } from "@bufbuild/protobuf";
4
+ import { createClient, type Client } from "@connectrpc/connect";
5
+ import { createGrpcTransport } from "@connectrpc/connect-node";
6
+
7
+ import {
8
+ WorkflowManagerCreateScheduleRequestSchema,
9
+ WorkflowManagerDeleteScheduleRequestSchema,
10
+ WorkflowManagerGetScheduleRequestSchema,
11
+ WorkflowManagerHost as WorkflowManagerHostService,
12
+ WorkflowManagerPauseScheduleRequestSchema,
13
+ WorkflowManagerResumeScheduleRequestSchema,
14
+ WorkflowManagerUpdateScheduleRequestSchema,
15
+ type ManagedWorkflowSchedule,
16
+ } from "../gen/v1/workflow_pb.ts";
17
+ import type { Request } from "./api.ts";
18
+
19
+ export const ENV_WORKFLOW_MANAGER_SOCKET = "GESTALT_WORKFLOW_MANAGER_SOCKET";
20
+
21
+ export type ManagedWorkflowScheduleMessage = ManagedWorkflowSchedule;
22
+ export type WorkflowManagerCreateScheduleInput = MessageInitShape<
23
+ typeof WorkflowManagerCreateScheduleRequestSchema
24
+ >;
25
+ export type WorkflowManagerGetScheduleInput = MessageInitShape<
26
+ typeof WorkflowManagerGetScheduleRequestSchema
27
+ >;
28
+ export type WorkflowManagerUpdateScheduleInput = MessageInitShape<
29
+ typeof WorkflowManagerUpdateScheduleRequestSchema
30
+ >;
31
+ export type WorkflowManagerDeleteScheduleInput = MessageInitShape<
32
+ typeof WorkflowManagerDeleteScheduleRequestSchema
33
+ >;
34
+ export type WorkflowManagerPauseScheduleInput = MessageInitShape<
35
+ typeof WorkflowManagerPauseScheduleRequestSchema
36
+ >;
37
+ export type WorkflowManagerResumeScheduleInput = MessageInitShape<
38
+ typeof WorkflowManagerResumeScheduleRequestSchema
39
+ >;
40
+
41
+ export class WorkflowManager {
42
+ private readonly client: Client<typeof WorkflowManagerHostService>;
43
+ private readonly invocationToken: string;
44
+
45
+ constructor(request: Request);
46
+ constructor(invocationToken: string);
47
+ constructor(requestOrToken: Request | string) {
48
+ this.invocationToken = normalizeInvocationToken(requestOrToken);
49
+
50
+ const socketPath = process.env[ENV_WORKFLOW_MANAGER_SOCKET];
51
+ if (!socketPath) {
52
+ throw new Error(
53
+ `workflow manager: ${ENV_WORKFLOW_MANAGER_SOCKET} is not set`,
54
+ );
55
+ }
56
+
57
+ const transport = createGrpcTransport({
58
+ baseUrl: "http://localhost",
59
+ nodeOptions: {
60
+ createConnection: () => connect(socketPath),
61
+ },
62
+ });
63
+ this.client = createClient(WorkflowManagerHostService, transport);
64
+ }
65
+
66
+ async createSchedule(
67
+ request: WorkflowManagerCreateScheduleInput,
68
+ ): Promise<ManagedWorkflowScheduleMessage> {
69
+ return await this.client.createSchedule({
70
+ ...request,
71
+ invocationToken: this.invocationToken,
72
+ });
73
+ }
74
+
75
+ async getSchedule(
76
+ request: WorkflowManagerGetScheduleInput,
77
+ ): Promise<ManagedWorkflowScheduleMessage> {
78
+ return await this.client.getSchedule({
79
+ ...request,
80
+ invocationToken: this.invocationToken,
81
+ });
82
+ }
83
+
84
+ async updateSchedule(
85
+ request: WorkflowManagerUpdateScheduleInput,
86
+ ): Promise<ManagedWorkflowScheduleMessage> {
87
+ return await this.client.updateSchedule({
88
+ ...request,
89
+ invocationToken: this.invocationToken,
90
+ });
91
+ }
92
+
93
+ async deleteSchedule(
94
+ request: WorkflowManagerDeleteScheduleInput,
95
+ ): Promise<void> {
96
+ await this.client.deleteSchedule({
97
+ ...request,
98
+ invocationToken: this.invocationToken,
99
+ });
100
+ }
101
+
102
+ async pauseSchedule(
103
+ request: WorkflowManagerPauseScheduleInput,
104
+ ): Promise<ManagedWorkflowScheduleMessage> {
105
+ return await this.client.pauseSchedule({
106
+ ...request,
107
+ invocationToken: this.invocationToken,
108
+ });
109
+ }
110
+
111
+ async resumeSchedule(
112
+ request: WorkflowManagerResumeScheduleInput,
113
+ ): Promise<ManagedWorkflowScheduleMessage> {
114
+ return await this.client.resumeSchedule({
115
+ ...request,
116
+ invocationToken: this.invocationToken,
117
+ });
118
+ }
119
+ }
120
+
121
+ function normalizeInvocationToken(requestOrToken: Request | string): string {
122
+ const invocationToken =
123
+ typeof requestOrToken === "string"
124
+ ? requestOrToken
125
+ : requestOrToken.invocationToken;
126
+ const trimmed = invocationToken.trim();
127
+ if (!trimmed) {
128
+ throw new Error("workflow manager: invocation token is not available");
129
+ }
130
+ return trimmed;
131
+ }