shiftapi 0.0.15 → 0.0.17

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 CHANGED
@@ -59,7 +59,7 @@ Typically used in a `postinstall` script:
59
59
  |--------|------|---------|-------------|
60
60
  | `server` | `string` | **(required)** | Path to the Go server entry point (e.g. `"./cmd/server"`) |
61
61
  | `baseUrl` | `string` | `"/"` | Base URL for the generated API client |
62
- | `url` | `string` | `"http://localhost:8080"` | Go server address (used by the Vite plugin for dev proxy) |
62
+ | `url` | `string` | `"http://localhost:8080"` | Go server address (used by the Vite/Next.js plugins for dev proxy) |
63
63
 
64
64
  ## License
65
65
 
@@ -80,6 +80,36 @@ export const client = createClient({
80
80
  baseUrl: ${JSON.stringify(baseUrl)},
81
81
  });
82
82
 
83
+ export { createClient };
84
+ `;
85
+ }
86
+ function nextClientJsTemplate(port, baseUrl, devApiPrefix) {
87
+ if (!devApiPrefix) {
88
+ return `// Auto-generated by @shiftapi/next. Do not edit.
89
+ import createClient from "openapi-fetch";
90
+
91
+ const baseUrl =
92
+ process.env.NEXT_PUBLIC_SHIFTAPI_BASE_URL || ${JSON.stringify(baseUrl)};
93
+
94
+ /** Pre-configured, fully-typed API client. */
95
+ export const client = createClient({ baseUrl });
96
+
97
+ export { createClient };
98
+ `;
99
+ }
100
+ const devServerUrl = `http://localhost:${port}`;
101
+ return `// Auto-generated by @shiftapi/next. Do not edit.
102
+ import createClient from "openapi-fetch";
103
+
104
+ const baseUrl =
105
+ process.env.NEXT_PUBLIC_SHIFTAPI_BASE_URL ||
106
+ (typeof window !== "undefined"
107
+ ? ${JSON.stringify(devApiPrefix)}
108
+ : ${JSON.stringify(devServerUrl)});
109
+
110
+ /** Pre-configured, fully-typed API client. */
111
+ export const client = createClient({ baseUrl });
112
+
83
113
  export { createClient };
84
114
  `;
85
115
  }
@@ -111,13 +141,13 @@ async function regenerateTypes(serverEntry, goRoot, baseUrl, isDev, previousType
111
141
  );
112
142
  return { types, virtualModuleSource, changed };
113
143
  }
114
- function writeGeneratedFiles(typesRoot, generatedDts, baseUrl) {
144
+ function writeGeneratedFiles(typesRoot, generatedDts, baseUrl, options) {
115
145
  const outDir = resolve(typesRoot, ".shiftapi");
116
146
  if (!existsSync(outDir)) {
117
147
  mkdirSync(outDir, { recursive: true });
118
148
  }
119
149
  writeFileSync(resolve(outDir, "client.d.ts"), dtsTemplate(generatedDts));
120
- writeFileSync(resolve(outDir, "client.js"), clientJsTemplate(baseUrl));
150
+ writeFileSync(resolve(outDir, "client.js"), options?.clientJsContent ?? clientJsTemplate(baseUrl));
121
151
  writeFileSync(
122
152
  resolve(outDir, "tsconfig.json"),
123
153
  JSON.stringify(
@@ -133,7 +163,7 @@ function writeGeneratedFiles(typesRoot, generatedDts, baseUrl) {
133
163
  ) + "\n"
134
164
  );
135
165
  }
136
- function patchTsConfig(tsconfigDir, typesRoot) {
166
+ function patchTsConfigPaths(tsconfigDir, typesRoot) {
137
167
  const tsconfigPath = resolve(tsconfigDir, "tsconfig.json");
138
168
  if (!existsSync(tsconfigPath)) return;
139
169
  const raw = readFileSync2(tsconfigPath, "utf-8");
@@ -146,14 +176,19 @@ function patchTsConfig(tsconfigDir, typesRoot) {
146
176
  );
147
177
  return;
148
178
  }
149
- const rel = relative(tsconfigDir, resolve(typesRoot, ".shiftapi", "tsconfig.json"));
150
- const extendsPath = rel.startsWith("..") ? rel : `./${rel}`;
151
- if (tsconfig?.extends === extendsPath) return;
152
- tsconfig.extends = extendsPath;
179
+ const dtsRel = relative(tsconfigDir, resolve(typesRoot, ".shiftapi", "client.d.ts"));
180
+ const dtsPath = dtsRel.startsWith("..") ? dtsRel : `./${dtsRel}`;
181
+ tsconfig.compilerOptions = tsconfig.compilerOptions || {};
182
+ tsconfig.compilerOptions.paths = tsconfig.compilerOptions.paths || {};
183
+ const existing = tsconfig.compilerOptions.paths[MODULE_ID];
184
+ if (Array.isArray(existing) && existing.length === 1 && existing[0] === dtsPath) {
185
+ return;
186
+ }
187
+ tsconfig.compilerOptions.paths[MODULE_ID] = [dtsPath];
153
188
  const detectedIndent = raw.match(/^[ \t]+/m)?.[0] ?? " ";
154
189
  writeFileSync(tsconfigPath, stringify(tsconfig, null, detectedIndent) + "\n");
155
190
  console.log(
156
- "[shiftapi] Updated tsconfig.json to extend .shiftapi/tsconfig.json"
191
+ "[shiftapi] Updated tsconfig.json with @shiftapi/client path mapping."
157
192
  );
158
193
  }
159
194
 
@@ -165,8 +200,9 @@ export {
165
200
  DEV_API_PREFIX,
166
201
  dtsTemplate,
167
202
  clientJsTemplate,
203
+ nextClientJsTemplate,
168
204
  virtualModuleTemplate,
169
205
  regenerateTypes,
170
206
  writeGeneratedFiles,
171
- patchTsConfig
207
+ patchTsConfigPaths
172
208
  };
@@ -37,6 +37,14 @@ async function loadConfig(startDir, configPath) {
37
37
  }
38
38
  return { config, configDir: dirname(resolvedPath) };
39
39
  }
40
+ function findConfigDir(startDir, configPath) {
41
+ if (configPath) {
42
+ const resolved = resolve(startDir, configPath);
43
+ return existsSync(resolved) ? dirname(resolved) : void 0;
44
+ }
45
+ const file = findConfigFile(startDir);
46
+ return file ? dirname(file) : void 0;
47
+ }
40
48
  function findConfigFile(startDir) {
41
49
  let dir = resolve(startDir);
42
50
  const root = resolve("/");
@@ -57,5 +65,6 @@ function findConfigFile(startDir) {
57
65
 
58
66
  export {
59
67
  defineConfig,
60
- loadConfig
68
+ loadConfig,
69
+ findConfigDir
61
70
  };
@@ -22,5 +22,14 @@ declare function loadConfig(startDir: string, configPath?: string): Promise<{
22
22
  config: ShiftAPIConfig;
23
23
  configDir: string;
24
24
  }>;
25
+ interface ShiftAPIPluginOptions {
26
+ /** Explicit path to shiftapi.config.ts (for advanced use only) */
27
+ configPath?: string;
28
+ }
29
+ /**
30
+ * Synchronously walk up from `startDir` to find the directory containing
31
+ * a shiftapi config file. Returns the directory path, or undefined.
32
+ */
33
+ declare function findConfigDir(startDir: string, configPath?: string): string | undefined;
25
34
 
26
- export { type ShiftAPIConfig as S, defineConfig as d, loadConfig as l };
35
+ export { type ShiftAPIConfig as S, type ShiftAPIPluginOptions as a, defineConfig as d, findConfigDir as f, loadConfig as l };
package/dist/index.d.ts CHANGED
@@ -1 +1 @@
1
- export { S as ShiftAPIConfig, d as defineConfig } from './index-PUAivaXi.js';
1
+ export { S as ShiftAPIConfig, d as defineConfig } from './index-BG8_bsaE.js';
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  defineConfig
3
- } from "./chunk-QBXBYTBE.js";
3
+ } from "./chunk-EQN44FQG.js";
4
4
  export {
5
5
  defineConfig
6
6
  };
@@ -1,12 +1,14 @@
1
- export { S as ShiftAPIConfig, d as defineConfig, l as loadConfig } from './index-PUAivaXi.js';
1
+ export { S as ShiftAPIConfig, a as ShiftAPIPluginOptions, d as defineConfig, f as findConfigDir, l as loadConfig } from './index-BG8_bsaE.js';
2
2
 
3
3
  declare function regenerateTypes(serverEntry: string, goRoot: string, baseUrl: string, isDev: boolean, previousTypes: string): Promise<{
4
4
  types: string;
5
5
  virtualModuleSource: string;
6
6
  changed: boolean;
7
7
  }>;
8
- declare function writeGeneratedFiles(typesRoot: string, generatedDts: string, baseUrl: string): void;
9
- declare function patchTsConfig(tsconfigDir: string, typesRoot: string): void;
8
+ declare function writeGeneratedFiles(typesRoot: string, generatedDts: string, baseUrl: string, options?: {
9
+ clientJsContent?: string;
10
+ }): void;
11
+ declare function patchTsConfigPaths(tsconfigDir: string, typesRoot: string): void;
10
12
 
11
13
  /**
12
14
  * Extracts the OpenAPI spec from a Go shiftapi server by running it with
@@ -23,10 +25,23 @@ declare function generateTypes(spec: object): Promise<string>;
23
25
 
24
26
  declare function dtsTemplate(generatedTypes: string): string;
25
27
  declare function clientJsTemplate(baseUrl: string): string;
28
+ declare function nextClientJsTemplate(port: number, baseUrl: string, devApiPrefix?: string): string;
26
29
  declare function virtualModuleTemplate(baseUrl: string, devApiPrefix?: string): string;
27
30
 
28
31
  declare const MODULE_ID = "@shiftapi/client";
29
32
  declare const RESOLVED_MODULE_ID: string;
30
33
  declare const DEV_API_PREFIX = "/__shiftapi";
31
34
 
32
- export { DEV_API_PREFIX, MODULE_ID, RESOLVED_MODULE_ID, clientJsTemplate, dtsTemplate, extractSpec, generateTypes, patchTsConfig, regenerateTypes, virtualModuleTemplate, writeGeneratedFiles };
35
+ declare class GoServerManager {
36
+ private readonly serverEntry;
37
+ private readonly goRoot;
38
+ private goProcess;
39
+ constructor(serverEntry: string, goRoot: string);
40
+ start(port: number): Promise<void>;
41
+ stop(): Promise<void>;
42
+ forceKill(): void;
43
+ }
44
+
45
+ declare function findFreePort(startPort: number): Promise<number>;
46
+
47
+ export { DEV_API_PREFIX, GoServerManager, MODULE_ID, RESOLVED_MODULE_ID, clientJsTemplate, dtsTemplate, extractSpec, findFreePort, generateTypes, nextClientJsTemplate, patchTsConfigPaths, regenerateTypes, virtualModuleTemplate, writeGeneratedFiles };
package/dist/internal.js CHANGED
@@ -6,26 +6,139 @@ import {
6
6
  dtsTemplate,
7
7
  extractSpec,
8
8
  generateTypes,
9
- patchTsConfig,
9
+ nextClientJsTemplate,
10
+ patchTsConfigPaths,
10
11
  regenerateTypes,
11
12
  virtualModuleTemplate,
12
13
  writeGeneratedFiles
13
- } from "./chunk-M6SOET3R.js";
14
+ } from "./chunk-DQX3QC7F.js";
14
15
  import {
15
16
  defineConfig,
17
+ findConfigDir,
16
18
  loadConfig
17
- } from "./chunk-QBXBYTBE.js";
19
+ } from "./chunk-EQN44FQG.js";
20
+
21
+ // src/goServer.ts
22
+ import { spawn } from "child_process";
23
+ import { resolve } from "path";
24
+ var GoServerManager = class {
25
+ constructor(serverEntry, goRoot) {
26
+ this.serverEntry = serverEntry;
27
+ this.goRoot = goRoot;
28
+ }
29
+ goProcess = null;
30
+ start(port) {
31
+ return new Promise((resolveStart, rejectStart) => {
32
+ const proc = spawn(
33
+ "go",
34
+ ["run", "-tags", "shiftapidev", this.serverEntry],
35
+ {
36
+ cwd: resolve(this.goRoot),
37
+ stdio: ["ignore", "inherit", "inherit"],
38
+ detached: true,
39
+ env: {
40
+ ...process.env,
41
+ SHIFTAPI_PORT: String(port)
42
+ }
43
+ }
44
+ );
45
+ this.goProcess = proc;
46
+ let settled = false;
47
+ proc.on("error", (err) => {
48
+ console.error("[shiftapi] Failed to start Go server:", err.message);
49
+ if (!settled) {
50
+ settled = true;
51
+ rejectStart(err);
52
+ }
53
+ });
54
+ proc.on("exit", (code) => {
55
+ if (code !== null && code !== 0) {
56
+ console.error(`[shiftapi] Go server exited with code ${code}`);
57
+ }
58
+ this.goProcess = null;
59
+ });
60
+ proc.on("spawn", () => {
61
+ if (!settled) {
62
+ settled = true;
63
+ resolveStart();
64
+ }
65
+ });
66
+ console.log(
67
+ `[shiftapi] Go server starting on port ${port}: go run ${this.serverEntry}`
68
+ );
69
+ });
70
+ }
71
+ stop() {
72
+ const proc = this.goProcess;
73
+ if (!proc || !proc.pid) return Promise.resolve();
74
+ const pid = proc.pid;
75
+ this.goProcess = null;
76
+ return new Promise((resolve2) => {
77
+ const timeout = setTimeout(() => {
78
+ try {
79
+ process.kill(-pid, "SIGKILL");
80
+ } catch {
81
+ }
82
+ resolve2();
83
+ }, 5e3);
84
+ proc.on("exit", () => {
85
+ clearTimeout(timeout);
86
+ resolve2();
87
+ });
88
+ try {
89
+ process.kill(-pid, "SIGTERM");
90
+ } catch {
91
+ clearTimeout(timeout);
92
+ resolve2();
93
+ }
94
+ });
95
+ }
96
+ forceKill() {
97
+ if (this.goProcess?.pid) {
98
+ try {
99
+ process.kill(-this.goProcess.pid, "SIGTERM");
100
+ } catch {
101
+ }
102
+ }
103
+ }
104
+ };
105
+
106
+ // src/ports.ts
107
+ import { createServer as createTcpServer } from "net";
108
+ function isPortFree(port) {
109
+ return new Promise((resolve2) => {
110
+ const server = createTcpServer();
111
+ server.once("error", () => resolve2(false));
112
+ server.once("listening", () => {
113
+ server.close(() => resolve2(true));
114
+ });
115
+ server.listen(port);
116
+ });
117
+ }
118
+ async function findFreePort(startPort) {
119
+ for (let port = startPort; port < startPort + 20; port++) {
120
+ if (await isPortFree(port)) return port;
121
+ }
122
+ console.warn(
123
+ `[shiftapi] No free port found in range ${startPort}-${startPort + 19}, falling back to ${startPort}`
124
+ );
125
+ return startPort;
126
+ }
18
127
  export {
19
128
  DEV_API_PREFIX,
129
+ GoServerManager,
20
130
  MODULE_ID,
21
131
  RESOLVED_MODULE_ID,
22
132
  clientJsTemplate,
23
133
  defineConfig,
24
134
  dtsTemplate,
25
135
  extractSpec,
136
+ findConfigDir,
137
+ findFreePort,
26
138
  generateTypes,
27
139
  loadConfig,
28
- patchTsConfig,
140
+ nextClientJsTemplate,
141
+ patchTsConfigPaths,
29
142
  regenerateTypes,
30
143
  virtualModuleTemplate,
31
144
  writeGeneratedFiles
package/dist/prepare.js CHANGED
@@ -2,12 +2,12 @@
2
2
  import {
3
3
  extractSpec,
4
4
  generateTypes,
5
- patchTsConfig,
5
+ patchTsConfigPaths,
6
6
  writeGeneratedFiles
7
- } from "./chunk-M6SOET3R.js";
7
+ } from "./chunk-DQX3QC7F.js";
8
8
  import {
9
9
  loadConfig
10
- } from "./chunk-QBXBYTBE.js";
10
+ } from "./chunk-EQN44FQG.js";
11
11
 
12
12
  // src/prepare.ts
13
13
  import { resolve } from "path";
@@ -26,7 +26,7 @@ async function main() {
26
26
  const spec = extractSpec(serverEntry, resolve(goRoot));
27
27
  const types = await generateTypes(spec);
28
28
  writeGeneratedFiles(configDir, types, baseUrl);
29
- patchTsConfig(cwd, configDir);
29
+ patchTsConfigPaths(cwd, configDir);
30
30
  console.log("[shiftapi] Done. Generated .shiftapi/client.d.ts and .shiftapi/client.js");
31
31
  }
32
32
  main().catch((err) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "shiftapi",
3
- "version": "0.0.15",
3
+ "version": "0.0.17",
4
4
  "description": "CLI and codegen for shiftapi – fully-typed TypeScript clients from Go servers",
5
5
  "author": "Frank Chiarulli Jr. <frank@frankchiarulli.com>",
6
6
  "license": "MIT",