@stardeck-customer-apps/data-store-sdk 0.2.1 → 0.3.0
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/SKILL.md +15 -3
- package/dist/index.d.mts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/server/index.d.mts +48 -5
- package/dist/server/index.d.ts +48 -5
- package/dist/server/index.js +80 -4
- package/dist/server/index.mjs +78 -4
- package/dist/{types-CqVD-TLQ.d.mts → types-D2D9kZNF.d.mts} +20 -3
- package/dist/{types-CqVD-TLQ.d.ts → types-D2D9kZNF.d.ts} +20 -3
- package/package.json +1 -1
package/SKILL.md
CHANGED
|
@@ -38,7 +38,12 @@ Server Code
|
|
|
38
38
|
Data Store Neon Database (direct connection)
|
|
39
39
|
```
|
|
40
40
|
|
|
41
|
-
|
|
41
|
+
Connection info is injected in all environments (production, preview, sandbox) via the
|
|
42
|
+
`STARDECK_DATA_STORES` manifest (a JSON array of `{ id, name, slug, url, accessLevel }`,
|
|
43
|
+
one entry per connected store). The SDK resolves a store against this manifest by **stable
|
|
44
|
+
slug or id**, so a binding keeps working even after the store is renamed. The legacy
|
|
45
|
+
per-store `DATA_STORE_{NAME}_URL` / `DATA_STORE_{NAME}_ID` keys are still injected for
|
|
46
|
+
backward compatibility — prefer resolving by `storeName`/`storeId` through the SDK.
|
|
42
47
|
|
|
43
48
|
**Schema Management (via Platform API):**
|
|
44
49
|
|
|
@@ -80,6 +85,12 @@ DEPLOYMENT_SECRET=your-secret-key # HMAC signing (NEVER expose to cli
|
|
|
80
85
|
ORGANIZATION_ID=org_123
|
|
81
86
|
PROJECT_ID=proj_456
|
|
82
87
|
DEPLOYMENT_ID=deploy_789
|
|
88
|
+
|
|
89
|
+
# Canonical: one JSON entry per connected store. Resolve through the SDK by
|
|
90
|
+
# storeName/storeId — don't parse this by hand.
|
|
91
|
+
STARDECK_DATA_STORES=[{"id":"ds-abc123","name":"My Store","slug":"mystore","url":"postgres://...","accessLevel":"write"}]
|
|
92
|
+
|
|
93
|
+
# Legacy per-store keys (still injected for back-compat with older SDKs):
|
|
83
94
|
DATA_STORE_MYSTORE_URL=postgres://... # Per-store connection string (for Kysely)
|
|
84
95
|
DATA_STORE_MYSTORE_ID=ds-abc123 # Per-store ID (for DataStoreClient)
|
|
85
96
|
```
|
|
@@ -95,8 +106,9 @@ Use this for schema operations, agent-driven data access, or when you don't have
|
|
|
95
106
|
import { DataStoreClient } from "@stardeck-customer-apps/data-store-sdk/server";
|
|
96
107
|
|
|
97
108
|
export const dataStore = new DataStoreClient({
|
|
98
|
-
|
|
99
|
-
// Other config read from env vars
|
|
109
|
+
// Resolve the store id from its name/slug via the injected manifest (survives
|
|
110
|
+
// renames). Or pass storeId directly. Other config is read from env vars.
|
|
111
|
+
storeName: "mystore",
|
|
100
112
|
});
|
|
101
113
|
```
|
|
102
114
|
|
package/dist/index.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { C as ColumnDefinition, D as DataStoreClientConfig, F as FilterOperator, L as ListObjectsResult, Q as QueryFilter, b as QueryOptions, c as QueryResult, S as StorageObject, T as TableColumn, a as TableSchema } from './types-
|
|
1
|
+
export { C as ColumnDefinition, D as DataStoreClientConfig, F as FilterOperator, L as ListObjectsResult, Q as QueryFilter, b as QueryOptions, c as QueryResult, S as StorageObject, T as TableColumn, a as TableSchema } from './types-D2D9kZNF.mjs';
|
|
2
2
|
|
|
3
3
|
declare class DataStoreError extends Error {
|
|
4
4
|
code: string;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { C as ColumnDefinition, D as DataStoreClientConfig, F as FilterOperator, L as ListObjectsResult, Q as QueryFilter, b as QueryOptions, c as QueryResult, S as StorageObject, T as TableColumn, a as TableSchema } from './types-
|
|
1
|
+
export { C as ColumnDefinition, D as DataStoreClientConfig, F as FilterOperator, L as ListObjectsResult, Q as QueryFilter, b as QueryOptions, c as QueryResult, S as StorageObject, T as TableColumn, a as TableSchema } from './types-D2D9kZNF.js';
|
|
2
2
|
|
|
3
3
|
declare class DataStoreError extends Error {
|
|
4
4
|
code: string;
|
package/dist/server/index.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { D as DataStoreClientConfig, a as TableSchema, C as ColumnDefinition, b as QueryOptions, c as QueryResult, L as ListObjectsResult } from '../types-
|
|
1
|
+
import { D as DataStoreClientConfig, a as TableSchema, C as ColumnDefinition, b as QueryOptions, c as QueryResult, L as ListObjectsResult } from '../types-D2D9kZNF.mjs';
|
|
2
2
|
import { KyselyConfig, Kysely } from 'kysely';
|
|
3
3
|
|
|
4
4
|
/**
|
|
@@ -91,13 +91,17 @@ declare function signDeploymentRequest(deploymentSecret: string, payload: {
|
|
|
91
91
|
* import { createDataStore } from "@stardeck-customer-apps/data-store-sdk/server";
|
|
92
92
|
* import type { DB } from "./generated/data-store-types";
|
|
93
93
|
*
|
|
94
|
-
* // Option 1:
|
|
94
|
+
* // Option 1: Resolve by store name (matched against the stable slug in the
|
|
95
|
+
* // injected manifest, so it survives store renames)
|
|
95
96
|
* const db = await createDataStore<DB>({ storeName: "mystore" });
|
|
96
97
|
*
|
|
97
|
-
* // Option 2:
|
|
98
|
+
* // Option 2: Resolve by immutable store id
|
|
99
|
+
* const db = await createDataStore<DB>({ storeId: process.env.DATA_STORE_MYSTORE_ID });
|
|
100
|
+
*
|
|
101
|
+
* // Option 3: Pass connection string directly
|
|
98
102
|
* const db = await createDataStore<DB>({ connectionString: process.env.DATA_STORE_MYSTORE_URL });
|
|
99
103
|
*
|
|
100
|
-
* // Option
|
|
104
|
+
* // Option 4: Falls back to DATA_STORE_URL env var
|
|
101
105
|
* const db = await createDataStore<DB>();
|
|
102
106
|
*
|
|
103
107
|
* const users = await db
|
|
@@ -110,7 +114,46 @@ declare function signDeploymentRequest(deploymentSecret: string, payload: {
|
|
|
110
114
|
declare function createDataStore<DB>(options?: {
|
|
111
115
|
connectionString?: string;
|
|
112
116
|
storeName?: string;
|
|
117
|
+
storeId?: string;
|
|
113
118
|
kyselyConfig?: Partial<KyselyConfig>;
|
|
114
119
|
}): Promise<Kysely<DB>>;
|
|
115
120
|
|
|
116
|
-
|
|
121
|
+
/**
|
|
122
|
+
* Reads and resolves the data store manifest the platform injects into a
|
|
123
|
+
* deployed app.
|
|
124
|
+
*
|
|
125
|
+
* The platform injects a single env var, `STARDECK_DATA_STORES`, holding a JSON
|
|
126
|
+
* array of one entry per connected store: a stable `slug`, the store `id`, the
|
|
127
|
+
* current `name`, the connection `url`, and the `accessLevel`. Resolving against
|
|
128
|
+
* this manifest replaces the old `DATA_STORE_{NAME}_URL` key reconstruction — no
|
|
129
|
+
* fragile, must-match-the-injector key derivation on this side.
|
|
130
|
+
*
|
|
131
|
+
* This file intentionally mirrors `packages/lib/src/shared/data-store-manifest.ts`
|
|
132
|
+
* in the platform monorepo. The SDK is published and installed inside customer
|
|
133
|
+
* sandboxes, so it cannot import the workspace; the slug normalization here MUST
|
|
134
|
+
* stay in sync with the producer's `dataStoreSlug`.
|
|
135
|
+
*
|
|
136
|
+
* Everything degrades gracefully: a missing or malformed manifest resolves to an
|
|
137
|
+
* empty list, and callers fall back to the legacy per-store env keys.
|
|
138
|
+
*/
|
|
139
|
+
interface DataStoreManifestEntry {
|
|
140
|
+
id: string;
|
|
141
|
+
name: string;
|
|
142
|
+
slug: string;
|
|
143
|
+
url: string;
|
|
144
|
+
accessLevel: "read" | "write" | "admin";
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Resolves a store from the manifest by id, slug, or name. Exact id/slug/name
|
|
148
|
+
* matches win; a normalized slug match is the forgiving fallback so app code
|
|
149
|
+
* that passes a human store name (`storeName: "My Store"`) still resolves to the
|
|
150
|
+
* entry whose slug is `my_store`. Returns `undefined` if nothing matches.
|
|
151
|
+
*/
|
|
152
|
+
declare function resolveDataStore(ref: {
|
|
153
|
+
storeId?: string;
|
|
154
|
+
storeName?: string;
|
|
155
|
+
}): DataStoreManifestEntry | undefined;
|
|
156
|
+
/** Returns every connected store in the manifest (for enumeration/discovery). */
|
|
157
|
+
declare function listDataStores(): DataStoreManifestEntry[];
|
|
158
|
+
|
|
159
|
+
export { DataStoreClient, type DataStoreManifestEntry, createDataStore, listDataStores, resolveDataStore, signDeploymentRequest };
|
package/dist/server/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { D as DataStoreClientConfig, a as TableSchema, C as ColumnDefinition, b as QueryOptions, c as QueryResult, L as ListObjectsResult } from '../types-
|
|
1
|
+
import { D as DataStoreClientConfig, a as TableSchema, C as ColumnDefinition, b as QueryOptions, c as QueryResult, L as ListObjectsResult } from '../types-D2D9kZNF.js';
|
|
2
2
|
import { KyselyConfig, Kysely } from 'kysely';
|
|
3
3
|
|
|
4
4
|
/**
|
|
@@ -91,13 +91,17 @@ declare function signDeploymentRequest(deploymentSecret: string, payload: {
|
|
|
91
91
|
* import { createDataStore } from "@stardeck-customer-apps/data-store-sdk/server";
|
|
92
92
|
* import type { DB } from "./generated/data-store-types";
|
|
93
93
|
*
|
|
94
|
-
* // Option 1:
|
|
94
|
+
* // Option 1: Resolve by store name (matched against the stable slug in the
|
|
95
|
+
* // injected manifest, so it survives store renames)
|
|
95
96
|
* const db = await createDataStore<DB>({ storeName: "mystore" });
|
|
96
97
|
*
|
|
97
|
-
* // Option 2:
|
|
98
|
+
* // Option 2: Resolve by immutable store id
|
|
99
|
+
* const db = await createDataStore<DB>({ storeId: process.env.DATA_STORE_MYSTORE_ID });
|
|
100
|
+
*
|
|
101
|
+
* // Option 3: Pass connection string directly
|
|
98
102
|
* const db = await createDataStore<DB>({ connectionString: process.env.DATA_STORE_MYSTORE_URL });
|
|
99
103
|
*
|
|
100
|
-
* // Option
|
|
104
|
+
* // Option 4: Falls back to DATA_STORE_URL env var
|
|
101
105
|
* const db = await createDataStore<DB>();
|
|
102
106
|
*
|
|
103
107
|
* const users = await db
|
|
@@ -110,7 +114,46 @@ declare function signDeploymentRequest(deploymentSecret: string, payload: {
|
|
|
110
114
|
declare function createDataStore<DB>(options?: {
|
|
111
115
|
connectionString?: string;
|
|
112
116
|
storeName?: string;
|
|
117
|
+
storeId?: string;
|
|
113
118
|
kyselyConfig?: Partial<KyselyConfig>;
|
|
114
119
|
}): Promise<Kysely<DB>>;
|
|
115
120
|
|
|
116
|
-
|
|
121
|
+
/**
|
|
122
|
+
* Reads and resolves the data store manifest the platform injects into a
|
|
123
|
+
* deployed app.
|
|
124
|
+
*
|
|
125
|
+
* The platform injects a single env var, `STARDECK_DATA_STORES`, holding a JSON
|
|
126
|
+
* array of one entry per connected store: a stable `slug`, the store `id`, the
|
|
127
|
+
* current `name`, the connection `url`, and the `accessLevel`. Resolving against
|
|
128
|
+
* this manifest replaces the old `DATA_STORE_{NAME}_URL` key reconstruction — no
|
|
129
|
+
* fragile, must-match-the-injector key derivation on this side.
|
|
130
|
+
*
|
|
131
|
+
* This file intentionally mirrors `packages/lib/src/shared/data-store-manifest.ts`
|
|
132
|
+
* in the platform monorepo. The SDK is published and installed inside customer
|
|
133
|
+
* sandboxes, so it cannot import the workspace; the slug normalization here MUST
|
|
134
|
+
* stay in sync with the producer's `dataStoreSlug`.
|
|
135
|
+
*
|
|
136
|
+
* Everything degrades gracefully: a missing or malformed manifest resolves to an
|
|
137
|
+
* empty list, and callers fall back to the legacy per-store env keys.
|
|
138
|
+
*/
|
|
139
|
+
interface DataStoreManifestEntry {
|
|
140
|
+
id: string;
|
|
141
|
+
name: string;
|
|
142
|
+
slug: string;
|
|
143
|
+
url: string;
|
|
144
|
+
accessLevel: "read" | "write" | "admin";
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Resolves a store from the manifest by id, slug, or name. Exact id/slug/name
|
|
148
|
+
* matches win; a normalized slug match is the forgiving fallback so app code
|
|
149
|
+
* that passes a human store name (`storeName: "My Store"`) still resolves to the
|
|
150
|
+
* entry whose slug is `my_store`. Returns `undefined` if nothing matches.
|
|
151
|
+
*/
|
|
152
|
+
declare function resolveDataStore(ref: {
|
|
153
|
+
storeId?: string;
|
|
154
|
+
storeName?: string;
|
|
155
|
+
}): DataStoreManifestEntry | undefined;
|
|
156
|
+
/** Returns every connected store in the manifest (for enumeration/discovery). */
|
|
157
|
+
declare function listDataStores(): DataStoreManifestEntry[];
|
|
158
|
+
|
|
159
|
+
export { DataStoreClient, type DataStoreManifestEntry, createDataStore, listDataStores, resolveDataStore, signDeploymentRequest };
|
package/dist/server/index.js
CHANGED
|
@@ -32,6 +32,8 @@ var server_exports = {};
|
|
|
32
32
|
__export(server_exports, {
|
|
33
33
|
DataStoreClient: () => DataStoreClient,
|
|
34
34
|
createDataStore: () => createDataStore,
|
|
35
|
+
listDataStores: () => listDataStores,
|
|
36
|
+
resolveDataStore: () => resolveDataStore,
|
|
35
37
|
signDeploymentRequest: () => signDeploymentRequest
|
|
36
38
|
});
|
|
37
39
|
module.exports = __toCommonJS(server_exports);
|
|
@@ -71,6 +73,57 @@ function signDeploymentRequest(deploymentSecret, payload) {
|
|
|
71
73
|
return `${payloadB64}.${signature}`;
|
|
72
74
|
}
|
|
73
75
|
|
|
76
|
+
// src/server/manifest.ts
|
|
77
|
+
var STARDECK_DATA_STORES_ENV = "STARDECK_DATA_STORES";
|
|
78
|
+
function readEnv(key) {
|
|
79
|
+
if (typeof process !== "undefined" && process.env?.[key]) {
|
|
80
|
+
return process.env[key];
|
|
81
|
+
}
|
|
82
|
+
return void 0;
|
|
83
|
+
}
|
|
84
|
+
function dataStoreSlug(name) {
|
|
85
|
+
const slug = name.toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "");
|
|
86
|
+
return slug || "store";
|
|
87
|
+
}
|
|
88
|
+
function legacyDataStoreEnvName(name) {
|
|
89
|
+
return name.toUpperCase().replace(/[^A-Z0-9]/g, "_");
|
|
90
|
+
}
|
|
91
|
+
function readDataStoreManifest() {
|
|
92
|
+
const raw = readEnv(STARDECK_DATA_STORES_ENV);
|
|
93
|
+
if (!raw) return [];
|
|
94
|
+
try {
|
|
95
|
+
const parsed = JSON.parse(raw);
|
|
96
|
+
if (!Array.isArray(parsed)) return [];
|
|
97
|
+
return parsed.filter(
|
|
98
|
+
(e) => typeof e === "object" && e !== null && typeof e.id === "string" && typeof e.name === "string" && typeof e.slug === "string" && typeof e.url === "string"
|
|
99
|
+
);
|
|
100
|
+
} catch {
|
|
101
|
+
return [];
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
function resolveDataStore(ref) {
|
|
105
|
+
return resolveManifestEntry(readDataStoreManifest(), ref);
|
|
106
|
+
}
|
|
107
|
+
function resolveManifestEntry(entries, ref) {
|
|
108
|
+
if (ref.storeId) {
|
|
109
|
+
const byId = entries.find((e) => e.id === ref.storeId);
|
|
110
|
+
if (byId) return byId;
|
|
111
|
+
}
|
|
112
|
+
if (ref.storeName) {
|
|
113
|
+
const target = ref.storeName;
|
|
114
|
+
const exact = entries.find((e) => e.slug === target || e.id === target || e.name === target);
|
|
115
|
+
if (exact) return exact;
|
|
116
|
+
const norm = dataStoreSlug(target);
|
|
117
|
+
return entries.find(
|
|
118
|
+
(e) => e.slug === norm || typeof e.name === "string" && dataStoreSlug(e.name) === norm
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
return void 0;
|
|
122
|
+
}
|
|
123
|
+
function listDataStores() {
|
|
124
|
+
return readDataStoreManifest();
|
|
125
|
+
}
|
|
126
|
+
|
|
74
127
|
// src/server/client.ts
|
|
75
128
|
var DataStoreClient = class {
|
|
76
129
|
baseUrl;
|
|
@@ -82,7 +135,7 @@ var DataStoreClient = class {
|
|
|
82
135
|
maxRetries;
|
|
83
136
|
debug;
|
|
84
137
|
constructor(config) {
|
|
85
|
-
this.storeId = config.storeId;
|
|
138
|
+
this.storeId = config.storeId || (config.storeName ? resolveDataStore({ storeName: config.storeName })?.id : void 0) || "";
|
|
86
139
|
this.baseUrl = config.baseUrl || this.getEnv("CONTROL_PLANE_URL") || "";
|
|
87
140
|
this.deploymentSecret = config.deploymentSecret || this.getEnv("DEPLOYMENT_SECRET") || "";
|
|
88
141
|
this.organizationId = config.organizationId || this.getEnv("ORGANIZATION_ID") || "";
|
|
@@ -91,6 +144,7 @@ var DataStoreClient = class {
|
|
|
91
144
|
this.maxRetries = config.maxRetries ?? 3;
|
|
92
145
|
this.debug = config.debug ?? false;
|
|
93
146
|
const missing = [
|
|
147
|
+
!this.storeId && "storeId (pass storeId/storeName, or connect the store so it appears in STARDECK_DATA_STORES)",
|
|
94
148
|
!this.baseUrl && "CONTROL_PLANE_URL",
|
|
95
149
|
!this.deploymentSecret && "DEPLOYMENT_SECRET",
|
|
96
150
|
!this.organizationId && "ORGANIZATION_ID",
|
|
@@ -246,12 +300,32 @@ var DataStoreClient = class {
|
|
|
246
300
|
|
|
247
301
|
// src/server/kysely.ts
|
|
248
302
|
var import_kysely = require("kysely");
|
|
303
|
+
function readEnv2(key) {
|
|
304
|
+
if (typeof process !== "undefined" && process.env?.[key]) {
|
|
305
|
+
return process.env[key];
|
|
306
|
+
}
|
|
307
|
+
return void 0;
|
|
308
|
+
}
|
|
309
|
+
function resolveConnectionString(options) {
|
|
310
|
+
if (options?.connectionString) return options.connectionString;
|
|
311
|
+
const fromManifest = resolveDataStore({
|
|
312
|
+
storeId: options?.storeId,
|
|
313
|
+
storeName: options?.storeName
|
|
314
|
+
})?.url;
|
|
315
|
+
if (fromManifest) return fromManifest;
|
|
316
|
+
if (options?.storeName) {
|
|
317
|
+
const legacy = readEnv2(`DATA_STORE_${legacyDataStoreEnvName(options.storeName)}_URL`);
|
|
318
|
+
if (legacy) return legacy;
|
|
319
|
+
}
|
|
320
|
+
return readEnv2("DATA_STORE_URL");
|
|
321
|
+
}
|
|
249
322
|
async function createDataStore(options) {
|
|
250
|
-
const connectionString =
|
|
323
|
+
const connectionString = resolveConnectionString(options);
|
|
251
324
|
if (!connectionString) {
|
|
252
|
-
const
|
|
325
|
+
const name = options?.storeName ?? options?.storeId;
|
|
326
|
+
const ref = name ? `data store "${name}"` : "a data store";
|
|
253
327
|
throw new Error(
|
|
254
|
-
|
|
328
|
+
`Could not resolve a connection string for ${ref}. Checked the STARDECK_DATA_STORES manifest, the legacy DATA_STORE_*_URL env var, and DATA_STORE_URL. Confirm the store is connected to this project, or pass connectionString in options.`
|
|
255
329
|
);
|
|
256
330
|
}
|
|
257
331
|
const { Kysely: KyselyClass } = await import("kysely");
|
|
@@ -269,5 +343,7 @@ async function createDataStore(options) {
|
|
|
269
343
|
0 && (module.exports = {
|
|
270
344
|
DataStoreClient,
|
|
271
345
|
createDataStore,
|
|
346
|
+
listDataStores,
|
|
347
|
+
resolveDataStore,
|
|
272
348
|
signDeploymentRequest
|
|
273
349
|
});
|
package/dist/server/index.mjs
CHANGED
|
@@ -20,6 +20,57 @@ function signDeploymentRequest(deploymentSecret, payload) {
|
|
|
20
20
|
return `${payloadB64}.${signature}`;
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
+
// src/server/manifest.ts
|
|
24
|
+
var STARDECK_DATA_STORES_ENV = "STARDECK_DATA_STORES";
|
|
25
|
+
function readEnv(key) {
|
|
26
|
+
if (typeof process !== "undefined" && process.env?.[key]) {
|
|
27
|
+
return process.env[key];
|
|
28
|
+
}
|
|
29
|
+
return void 0;
|
|
30
|
+
}
|
|
31
|
+
function dataStoreSlug(name) {
|
|
32
|
+
const slug = name.toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "");
|
|
33
|
+
return slug || "store";
|
|
34
|
+
}
|
|
35
|
+
function legacyDataStoreEnvName(name) {
|
|
36
|
+
return name.toUpperCase().replace(/[^A-Z0-9]/g, "_");
|
|
37
|
+
}
|
|
38
|
+
function readDataStoreManifest() {
|
|
39
|
+
const raw = readEnv(STARDECK_DATA_STORES_ENV);
|
|
40
|
+
if (!raw) return [];
|
|
41
|
+
try {
|
|
42
|
+
const parsed = JSON.parse(raw);
|
|
43
|
+
if (!Array.isArray(parsed)) return [];
|
|
44
|
+
return parsed.filter(
|
|
45
|
+
(e) => typeof e === "object" && e !== null && typeof e.id === "string" && typeof e.name === "string" && typeof e.slug === "string" && typeof e.url === "string"
|
|
46
|
+
);
|
|
47
|
+
} catch {
|
|
48
|
+
return [];
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
function resolveDataStore(ref) {
|
|
52
|
+
return resolveManifestEntry(readDataStoreManifest(), ref);
|
|
53
|
+
}
|
|
54
|
+
function resolveManifestEntry(entries, ref) {
|
|
55
|
+
if (ref.storeId) {
|
|
56
|
+
const byId = entries.find((e) => e.id === ref.storeId);
|
|
57
|
+
if (byId) return byId;
|
|
58
|
+
}
|
|
59
|
+
if (ref.storeName) {
|
|
60
|
+
const target = ref.storeName;
|
|
61
|
+
const exact = entries.find((e) => e.slug === target || e.id === target || e.name === target);
|
|
62
|
+
if (exact) return exact;
|
|
63
|
+
const norm = dataStoreSlug(target);
|
|
64
|
+
return entries.find(
|
|
65
|
+
(e) => e.slug === norm || typeof e.name === "string" && dataStoreSlug(e.name) === norm
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
return void 0;
|
|
69
|
+
}
|
|
70
|
+
function listDataStores() {
|
|
71
|
+
return readDataStoreManifest();
|
|
72
|
+
}
|
|
73
|
+
|
|
23
74
|
// src/server/client.ts
|
|
24
75
|
var DataStoreClient = class {
|
|
25
76
|
baseUrl;
|
|
@@ -31,7 +82,7 @@ var DataStoreClient = class {
|
|
|
31
82
|
maxRetries;
|
|
32
83
|
debug;
|
|
33
84
|
constructor(config) {
|
|
34
|
-
this.storeId = config.storeId;
|
|
85
|
+
this.storeId = config.storeId || (config.storeName ? resolveDataStore({ storeName: config.storeName })?.id : void 0) || "";
|
|
35
86
|
this.baseUrl = config.baseUrl || this.getEnv("CONTROL_PLANE_URL") || "";
|
|
36
87
|
this.deploymentSecret = config.deploymentSecret || this.getEnv("DEPLOYMENT_SECRET") || "";
|
|
37
88
|
this.organizationId = config.organizationId || this.getEnv("ORGANIZATION_ID") || "";
|
|
@@ -40,6 +91,7 @@ var DataStoreClient = class {
|
|
|
40
91
|
this.maxRetries = config.maxRetries ?? 3;
|
|
41
92
|
this.debug = config.debug ?? false;
|
|
42
93
|
const missing = [
|
|
94
|
+
!this.storeId && "storeId (pass storeId/storeName, or connect the store so it appears in STARDECK_DATA_STORES)",
|
|
43
95
|
!this.baseUrl && "CONTROL_PLANE_URL",
|
|
44
96
|
!this.deploymentSecret && "DEPLOYMENT_SECRET",
|
|
45
97
|
!this.organizationId && "ORGANIZATION_ID",
|
|
@@ -195,12 +247,32 @@ var DataStoreClient = class {
|
|
|
195
247
|
|
|
196
248
|
// src/server/kysely.ts
|
|
197
249
|
import "kysely";
|
|
250
|
+
function readEnv2(key) {
|
|
251
|
+
if (typeof process !== "undefined" && process.env?.[key]) {
|
|
252
|
+
return process.env[key];
|
|
253
|
+
}
|
|
254
|
+
return void 0;
|
|
255
|
+
}
|
|
256
|
+
function resolveConnectionString(options) {
|
|
257
|
+
if (options?.connectionString) return options.connectionString;
|
|
258
|
+
const fromManifest = resolveDataStore({
|
|
259
|
+
storeId: options?.storeId,
|
|
260
|
+
storeName: options?.storeName
|
|
261
|
+
})?.url;
|
|
262
|
+
if (fromManifest) return fromManifest;
|
|
263
|
+
if (options?.storeName) {
|
|
264
|
+
const legacy = readEnv2(`DATA_STORE_${legacyDataStoreEnvName(options.storeName)}_URL`);
|
|
265
|
+
if (legacy) return legacy;
|
|
266
|
+
}
|
|
267
|
+
return readEnv2("DATA_STORE_URL");
|
|
268
|
+
}
|
|
198
269
|
async function createDataStore(options) {
|
|
199
|
-
const connectionString =
|
|
270
|
+
const connectionString = resolveConnectionString(options);
|
|
200
271
|
if (!connectionString) {
|
|
201
|
-
const
|
|
272
|
+
const name = options?.storeName ?? options?.storeId;
|
|
273
|
+
const ref = name ? `data store "${name}"` : "a data store";
|
|
202
274
|
throw new Error(
|
|
203
|
-
|
|
275
|
+
`Could not resolve a connection string for ${ref}. Checked the STARDECK_DATA_STORES manifest, the legacy DATA_STORE_*_URL env var, and DATA_STORE_URL. Confirm the store is connected to this project, or pass connectionString in options.`
|
|
204
276
|
);
|
|
205
277
|
}
|
|
206
278
|
const { Kysely: KyselyClass } = await import("kysely");
|
|
@@ -217,5 +289,7 @@ async function createDataStore(options) {
|
|
|
217
289
|
export {
|
|
218
290
|
DataStoreClient,
|
|
219
291
|
createDataStore,
|
|
292
|
+
listDataStores,
|
|
293
|
+
resolveDataStore,
|
|
220
294
|
signDeploymentRequest
|
|
221
295
|
};
|
|
@@ -1,7 +1,24 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Identifies the store to operate on. Exactly one of `storeId` / `storeName` is
|
|
3
|
+
* required at compile time (both-optional would let `new DataStoreClient({})`
|
|
4
|
+
* type-check and then throw at runtime).
|
|
5
|
+
*/
|
|
6
|
+
type DataStoreRef = {
|
|
7
|
+
/** Immutable store id. */
|
|
8
|
+
storeId: string;
|
|
9
|
+
storeName?: string;
|
|
10
|
+
} | {
|
|
11
|
+
/**
|
|
12
|
+
* Store name or slug. Resolved to a store id via the injected
|
|
13
|
+
* `STARDECK_DATA_STORES` manifest (matched against the store's stable slug,
|
|
14
|
+
* so it survives renames).
|
|
15
|
+
*/
|
|
16
|
+
storeName: string;
|
|
17
|
+
storeId?: string;
|
|
18
|
+
};
|
|
19
|
+
type DataStoreClientConfig = DataStoreRef & {
|
|
2
20
|
/** Stardeck platform API base URL (default: CONTROL_PLANE_URL env var) */
|
|
3
21
|
baseUrl?: string;
|
|
4
|
-
storeId: string;
|
|
5
22
|
/** HMAC signing secret (default: DEPLOYMENT_SECRET env var) */
|
|
6
23
|
deploymentSecret?: string;
|
|
7
24
|
organizationId?: string;
|
|
@@ -9,7 +26,7 @@ interface DataStoreClientConfig {
|
|
|
9
26
|
deploymentId?: string;
|
|
10
27
|
maxRetries?: number;
|
|
11
28
|
debug?: boolean;
|
|
12
|
-
}
|
|
29
|
+
};
|
|
13
30
|
interface ColumnDefinition {
|
|
14
31
|
name: string;
|
|
15
32
|
fieldType: string;
|
|
@@ -1,7 +1,24 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Identifies the store to operate on. Exactly one of `storeId` / `storeName` is
|
|
3
|
+
* required at compile time (both-optional would let `new DataStoreClient({})`
|
|
4
|
+
* type-check and then throw at runtime).
|
|
5
|
+
*/
|
|
6
|
+
type DataStoreRef = {
|
|
7
|
+
/** Immutable store id. */
|
|
8
|
+
storeId: string;
|
|
9
|
+
storeName?: string;
|
|
10
|
+
} | {
|
|
11
|
+
/**
|
|
12
|
+
* Store name or slug. Resolved to a store id via the injected
|
|
13
|
+
* `STARDECK_DATA_STORES` manifest (matched against the store's stable slug,
|
|
14
|
+
* so it survives renames).
|
|
15
|
+
*/
|
|
16
|
+
storeName: string;
|
|
17
|
+
storeId?: string;
|
|
18
|
+
};
|
|
19
|
+
type DataStoreClientConfig = DataStoreRef & {
|
|
2
20
|
/** Stardeck platform API base URL (default: CONTROL_PLANE_URL env var) */
|
|
3
21
|
baseUrl?: string;
|
|
4
|
-
storeId: string;
|
|
5
22
|
/** HMAC signing secret (default: DEPLOYMENT_SECRET env var) */
|
|
6
23
|
deploymentSecret?: string;
|
|
7
24
|
organizationId?: string;
|
|
@@ -9,7 +26,7 @@ interface DataStoreClientConfig {
|
|
|
9
26
|
deploymentId?: string;
|
|
10
27
|
maxRetries?: number;
|
|
11
28
|
debug?: boolean;
|
|
12
|
-
}
|
|
29
|
+
};
|
|
13
30
|
interface ColumnDefinition {
|
|
14
31
|
name: string;
|
|
15
32
|
fieldType: string;
|