@cipherstash/stack 0.7.0 → 0.9.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/CHANGELOG.md +12 -0
- package/README.md +33 -3
- package/dist/bin/stash.js +419 -204
- package/dist/bin/stash.js.map +1 -1
- package/dist/{chunk-URZNWMOO.js → chunk-3BOJHROP.js} +3 -3
- package/dist/{chunk-H7IHWDZI.js → chunk-4C52Q5R6.js} +2 -2
- package/dist/{chunk-Y35W4WFN.js → chunk-Y3KSTOVR.js} +35 -4
- package/dist/chunk-Y3KSTOVR.js.map +1 -0
- package/dist/{client-DQMy1ux5.d.cts → client-CV7X5Hrt.d.cts} +1 -1
- package/dist/{client-zxrOpegN.d.ts → client-_63nbmO7.d.ts} +1 -1
- package/dist/client.cjs +14 -3
- package/dist/client.cjs.map +1 -1
- package/dist/client.d.cts +2 -2
- package/dist/client.d.ts +2 -2
- package/dist/client.js +1 -1
- package/dist/drizzle/index.cjs +14 -3
- package/dist/drizzle/index.cjs.map +1 -1
- package/dist/drizzle/index.d.cts +2 -2
- package/dist/drizzle/index.d.ts +2 -2
- package/dist/drizzle/index.js +1 -1
- package/dist/dynamodb/index.d.cts +2 -2
- package/dist/dynamodb/index.d.ts +2 -2
- package/dist/encryption/index.cjs +11 -0
- package/dist/encryption/index.cjs.map +1 -1
- package/dist/encryption/index.d.cts +2 -2
- package/dist/encryption/index.d.ts +2 -2
- package/dist/encryption/index.js +2 -2
- package/dist/index.cjs +14 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.js +3 -3
- package/dist/schema/index.cjs +38 -5
- package/dist/schema/index.cjs.map +1 -1
- package/dist/schema/index.d.cts +1 -1
- package/dist/schema/index.d.ts +1 -1
- package/dist/schema/index.js +7 -3
- package/dist/secrets/index.cjs +14 -3
- package/dist/secrets/index.cjs.map +1 -1
- package/dist/secrets/index.d.cts +1 -1
- package/dist/secrets/index.d.ts +1 -1
- package/dist/secrets/index.js +3 -3
- package/dist/supabase/index.cjs +28 -5
- package/dist/supabase/index.cjs.map +1 -1
- package/dist/supabase/index.d.cts +2 -2
- package/dist/supabase/index.d.ts +2 -2
- package/dist/supabase/index.js +17 -5
- package/dist/supabase/index.js.map +1 -1
- package/dist/{types-public-07ilcGY-.d.ts → types-public-B5YxQhR6.d.cts} +25 -9
- package/dist/{types-public-07ilcGY-.d.cts → types-public-B5YxQhR6.d.ts} +25 -9
- package/dist/types-public.d.cts +1 -1
- package/dist/types-public.d.ts +1 -1
- package/package.json +1 -1
- package/dist/chunk-Y35W4WFN.js.map +0 -1
- /package/dist/{chunk-URZNWMOO.js.map → chunk-3BOJHROP.js.map} +0 -0
- /package/dist/{chunk-H7IHWDZI.js.map → chunk-4C52Q5R6.js.map} +0 -0
package/dist/bin/stash.js
CHANGED
|
@@ -500,7 +500,7 @@ var require_picocolors = __commonJS({
|
|
|
500
500
|
// src/bin/stash.ts
|
|
501
501
|
var import_dotenv2 = __toESM(require_main(), 1);
|
|
502
502
|
import { readFileSync } from "node:fs";
|
|
503
|
-
import { dirname, join } from "node:path";
|
|
503
|
+
import { dirname as dirname2, join } from "node:path";
|
|
504
504
|
import { fileURLToPath } from "node:url";
|
|
505
505
|
|
|
506
506
|
// ../../node_modules/.pnpm/@clack+prompts@0.10.1/node_modules/@clack/prompts/dist/index.mjs
|
|
@@ -855,6 +855,41 @@ var dD = class extends x {
|
|
|
855
855
|
};
|
|
856
856
|
var A;
|
|
857
857
|
A = /* @__PURE__ */ new WeakMap();
|
|
858
|
+
var kD = Object.defineProperty;
|
|
859
|
+
var $D = (e2, u2, t) => u2 in e2 ? kD(e2, u2, { enumerable: true, configurable: true, writable: true, value: t }) : e2[u2] = t;
|
|
860
|
+
var H = (e2, u2, t) => ($D(e2, typeof u2 != "symbol" ? u2 + "" : u2, t), t);
|
|
861
|
+
var SD = class extends x {
|
|
862
|
+
constructor(u2) {
|
|
863
|
+
super(u2, false), H(this, "options"), H(this, "cursor", 0), this.options = u2.options, this.value = [...u2.initialValues ?? []], this.cursor = Math.max(this.options.findIndex(({ value: t }) => t === u2.cursorAt), 0), this.on("key", (t) => {
|
|
864
|
+
t === "a" && this.toggleAll();
|
|
865
|
+
}), this.on("cursor", (t) => {
|
|
866
|
+
switch (t) {
|
|
867
|
+
case "left":
|
|
868
|
+
case "up":
|
|
869
|
+
this.cursor = this.cursor === 0 ? this.options.length - 1 : this.cursor - 1;
|
|
870
|
+
break;
|
|
871
|
+
case "down":
|
|
872
|
+
case "right":
|
|
873
|
+
this.cursor = this.cursor === this.options.length - 1 ? 0 : this.cursor + 1;
|
|
874
|
+
break;
|
|
875
|
+
case "space":
|
|
876
|
+
this.toggleValue();
|
|
877
|
+
break;
|
|
878
|
+
}
|
|
879
|
+
});
|
|
880
|
+
}
|
|
881
|
+
get _value() {
|
|
882
|
+
return this.options[this.cursor].value;
|
|
883
|
+
}
|
|
884
|
+
toggleAll() {
|
|
885
|
+
const u2 = this.value.length === this.options.length;
|
|
886
|
+
this.value = u2 ? [] : this.options.map((t) => t.value);
|
|
887
|
+
}
|
|
888
|
+
toggleValue() {
|
|
889
|
+
const u2 = this.value.includes(this._value);
|
|
890
|
+
this.value = u2 ? this.value.filter((t) => t !== this._value) : [...this.value, this._value];
|
|
891
|
+
}
|
|
892
|
+
};
|
|
858
893
|
var OD = Object.defineProperty;
|
|
859
894
|
var PD = (e2, u2, t) => u2 in e2 ? OD(e2, u2, { enumerable: true, configurable: true, writable: true, value: t }) : e2[u2] = t;
|
|
860
895
|
var J = (e2, u2, t) => (PD(e2, typeof u2 != "symbol" ? u2 + "" : u2, t), t);
|
|
@@ -1023,6 +1058,46 @@ ${import_picocolors2.default.cyan(d2)}
|
|
|
1023
1058
|
}
|
|
1024
1059
|
} }).prompt();
|
|
1025
1060
|
};
|
|
1061
|
+
var fe = (t) => {
|
|
1062
|
+
const n = (r, i) => {
|
|
1063
|
+
const s = r.label ?? String(r.value);
|
|
1064
|
+
return i === "active" ? `${import_picocolors2.default.cyan(A2)} ${s} ${r.hint ? import_picocolors2.default.dim(`(${r.hint})`) : ""}` : i === "selected" ? `${import_picocolors2.default.green(T)} ${import_picocolors2.default.dim(s)} ${r.hint ? import_picocolors2.default.dim(`(${r.hint})`) : ""}` : i === "cancelled" ? `${import_picocolors2.default.strikethrough(import_picocolors2.default.dim(s))}` : i === "active-selected" ? `${import_picocolors2.default.green(T)} ${s} ${r.hint ? import_picocolors2.default.dim(`(${r.hint})`) : ""}` : i === "submitted" ? `${import_picocolors2.default.dim(s)}` : `${import_picocolors2.default.dim(F)} ${import_picocolors2.default.dim(s)}`;
|
|
1065
|
+
};
|
|
1066
|
+
return new SD({ options: t.options, initialValues: t.initialValues, required: t.required ?? true, cursorAt: t.cursorAt, validate(r) {
|
|
1067
|
+
if (this.required && r.length === 0) return `Please select at least one option.
|
|
1068
|
+
${import_picocolors2.default.reset(import_picocolors2.default.dim(`Press ${import_picocolors2.default.gray(import_picocolors2.default.bgWhite(import_picocolors2.default.inverse(" space ")))} to select, ${import_picocolors2.default.gray(import_picocolors2.default.bgWhite(import_picocolors2.default.inverse(" enter ")))} to submit`))}`;
|
|
1069
|
+
}, render() {
|
|
1070
|
+
const r = `${import_picocolors2.default.gray(o)}
|
|
1071
|
+
${b2(this.state)} ${t.message}
|
|
1072
|
+
`, i = (s, c) => {
|
|
1073
|
+
const a = this.value.includes(s.value);
|
|
1074
|
+
return c && a ? n(s, "active-selected") : a ? n(s, "selected") : n(s, c ? "active" : "inactive");
|
|
1075
|
+
};
|
|
1076
|
+
switch (this.state) {
|
|
1077
|
+
case "submit":
|
|
1078
|
+
return `${r}${import_picocolors2.default.gray(o)} ${this.options.filter(({ value: s }) => this.value.includes(s)).map((s) => n(s, "submitted")).join(import_picocolors2.default.dim(", ")) || import_picocolors2.default.dim("none")}`;
|
|
1079
|
+
case "cancel": {
|
|
1080
|
+
const s = this.options.filter(({ value: c }) => this.value.includes(c)).map((c) => n(c, "cancelled")).join(import_picocolors2.default.dim(", "));
|
|
1081
|
+
return `${r}${import_picocolors2.default.gray(o)} ${s.trim() ? `${s}
|
|
1082
|
+
${import_picocolors2.default.gray(o)}` : ""}`;
|
|
1083
|
+
}
|
|
1084
|
+
case "error": {
|
|
1085
|
+
const s = this.error.split(`
|
|
1086
|
+
`).map((c, a) => a === 0 ? `${import_picocolors2.default.yellow(d2)} ${import_picocolors2.default.yellow(c)}` : ` ${c}`).join(`
|
|
1087
|
+
`);
|
|
1088
|
+
return `${r + import_picocolors2.default.yellow(o)} ${G2({ options: this.options, cursor: this.cursor, maxItems: t.maxItems, style: i }).join(`
|
|
1089
|
+
${import_picocolors2.default.yellow(o)} `)}
|
|
1090
|
+
${s}
|
|
1091
|
+
`;
|
|
1092
|
+
}
|
|
1093
|
+
default:
|
|
1094
|
+
return `${r}${import_picocolors2.default.cyan(o)} ${G2({ options: this.options, cursor: this.cursor, maxItems: t.maxItems, style: i }).join(`
|
|
1095
|
+
${import_picocolors2.default.cyan(o)} `)}
|
|
1096
|
+
${import_picocolors2.default.cyan(d2)}
|
|
1097
|
+
`;
|
|
1098
|
+
}
|
|
1099
|
+
} }).prompt();
|
|
1100
|
+
};
|
|
1026
1101
|
var Me = (t = "", n = "") => {
|
|
1027
1102
|
const r = `
|
|
1028
1103
|
${t}
|
|
@@ -1097,7 +1172,7 @@ var Y2 = ({ indicator: t = "dots" } = {}) => {
|
|
|
1097
1172
|
}, R2 = (m2) => m2.replace(/\.+$/, ""), O2 = (m2) => {
|
|
1098
1173
|
const h2 = (performance.now() - m2) / 1e3, w2 = Math.floor(h2 / 60), I2 = Math.floor(h2 % 60);
|
|
1099
1174
|
return w2 > 0 ? `[${w2}m ${I2}s]` : `[${I2}s]`;
|
|
1100
|
-
},
|
|
1175
|
+
}, H2 = (m2 = "") => {
|
|
1101
1176
|
a = true, s = fD(), l2 = R2(m2), g2 = performance.now(), process.stdout.write(`${import_picocolors2.default.gray(o)}
|
|
1102
1177
|
`);
|
|
1103
1178
|
let h2 = 0, w2 = 0;
|
|
@@ -1120,7 +1195,7 @@ var Y2 = ({ indicator: t = "dots" } = {}) => {
|
|
|
1120
1195
|
`) : process.stdout.write(`${w2} ${l2}
|
|
1121
1196
|
`), E(), s();
|
|
1122
1197
|
};
|
|
1123
|
-
return { start:
|
|
1198
|
+
return { start: H2, stop: N2, message: (m2 = "") => {
|
|
1124
1199
|
l2 = R2(m2 ?? l2);
|
|
1125
1200
|
} };
|
|
1126
1201
|
};
|
|
@@ -1131,23 +1206,20 @@ function createBaseProvider() {
|
|
|
1131
1206
|
name: "base",
|
|
1132
1207
|
introMessage: "Setting up CipherStash for your project...",
|
|
1133
1208
|
connectionOptions: [
|
|
1134
|
-
{ value: "drizzle", label: "Drizzle ORM"
|
|
1209
|
+
{ value: "drizzle", label: "Drizzle ORM" },
|
|
1210
|
+
{ value: "supabase-js", label: "Supabase JS Client" },
|
|
1135
1211
|
{ value: "prisma", label: "Prisma" },
|
|
1136
1212
|
{ value: "raw-sql", label: "Raw SQL / pg" }
|
|
1137
1213
|
],
|
|
1138
1214
|
getNextSteps(state) {
|
|
1139
1215
|
const steps = [
|
|
1140
|
-
"
|
|
1216
|
+
"Create a CipherStash account and get your credentials:\n https://dashboard.cipherstash.com/sign-up\n Then set: CS_WORKSPACE_CRN, CS_CLIENT_ID, CS_CLIENT_KEY, CS_CLIENT_ACCESS_KEY",
|
|
1217
|
+
"Set up your database: npx stash-forge setup"
|
|
1141
1218
|
];
|
|
1142
|
-
if (state.
|
|
1143
|
-
steps.push(
|
|
1144
|
-
} else if (state.connectionMethod === "prisma") {
|
|
1145
|
-
steps.push("Set up Prisma with @cipherstash/stack");
|
|
1219
|
+
if (state.clientFilePath) {
|
|
1220
|
+
steps.push(`Edit your encryption schema: ${state.clientFilePath}`);
|
|
1146
1221
|
}
|
|
1147
|
-
steps.push(
|
|
1148
|
-
"Define your encrypted schema",
|
|
1149
|
-
"Read the docs: https://docs.cipherstash.com"
|
|
1150
|
-
);
|
|
1222
|
+
steps.push("Read the docs: https://cipherstash.com/docs");
|
|
1151
1223
|
return steps;
|
|
1152
1224
|
}
|
|
1153
1225
|
};
|
|
@@ -1170,20 +1242,14 @@ function createSupabaseProvider() {
|
|
|
1170
1242
|
],
|
|
1171
1243
|
getNextSteps(state) {
|
|
1172
1244
|
const steps = [
|
|
1173
|
-
"
|
|
1245
|
+
"Create a CipherStash account and get your credentials:\n https://dashboard.cipherstash.com/sign-up\n Then set: CS_WORKSPACE_CRN, CS_CLIENT_ID, CS_CLIENT_KEY, CS_CLIENT_ACCESS_KEY",
|
|
1246
|
+
"Set up your database: npx stash-forge setup"
|
|
1174
1247
|
];
|
|
1175
|
-
if (state.
|
|
1176
|
-
steps.push(
|
|
1177
|
-
} else if (state.connectionMethod === "drizzle") {
|
|
1178
|
-
steps.push("Import encryptedType from @cipherstash/stack/drizzle");
|
|
1179
|
-
} else if (state.connectionMethod === "prisma") {
|
|
1180
|
-
steps.push("Set up Prisma with @cipherstash/stack");
|
|
1248
|
+
if (state.clientFilePath) {
|
|
1249
|
+
steps.push(`Edit your encryption schema: ${state.clientFilePath}`);
|
|
1181
1250
|
}
|
|
1182
1251
|
steps.push(
|
|
1183
|
-
"
|
|
1184
|
-
"Supabase guides: https://cipherstash.com/docs/supabase/encrypt-user-data",
|
|
1185
|
-
"Multi-tenant encryption: https://docs.cipherstash.com/docs/multi-tenant",
|
|
1186
|
-
"Migrating existing data: https://docs.cipherstash.com/docs/migration",
|
|
1252
|
+
"Supabase guides: https://cipherstash.com/docs/stack/encryption/supabase",
|
|
1187
1253
|
"Need help? #supabase in Discord or support@cipherstash.com"
|
|
1188
1254
|
);
|
|
1189
1255
|
return steps;
|
|
@@ -1191,46 +1257,9 @@ function createSupabaseProvider() {
|
|
|
1191
1257
|
};
|
|
1192
1258
|
}
|
|
1193
1259
|
|
|
1194
|
-
// src/bin/commands/init/
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
}
|
|
1198
|
-
async function startDeviceCodeAuth() {
|
|
1199
|
-
await delay(500);
|
|
1200
|
-
return {
|
|
1201
|
-
verificationUrl: "https://cipherstash.com/activate",
|
|
1202
|
-
userCode: "ABCD-1234",
|
|
1203
|
-
deviceCode: "device_code_placeholder",
|
|
1204
|
-
expiresIn: 900,
|
|
1205
|
-
interval: 5
|
|
1206
|
-
};
|
|
1207
|
-
}
|
|
1208
|
-
async function pollForToken(deviceCode) {
|
|
1209
|
-
void deviceCode;
|
|
1210
|
-
await delay(2e3);
|
|
1211
|
-
return {
|
|
1212
|
-
accessToken: "stub_access_token",
|
|
1213
|
-
refreshToken: "stub_refresh_token",
|
|
1214
|
-
expiresIn: 3600
|
|
1215
|
-
};
|
|
1216
|
-
}
|
|
1217
|
-
async function fetchWorkspaces(accessToken) {
|
|
1218
|
-
void accessToken;
|
|
1219
|
-
await delay(300);
|
|
1220
|
-
return [
|
|
1221
|
-
{ id: "ws_1", name: "My First Workspace" },
|
|
1222
|
-
{ id: "ws_2", name: "Production" }
|
|
1223
|
-
];
|
|
1224
|
-
}
|
|
1225
|
-
async function createWorkspace(accessToken, name) {
|
|
1226
|
-
void accessToken;
|
|
1227
|
-
await delay(500);
|
|
1228
|
-
return { id: `ws_${Date.now()}`, name };
|
|
1229
|
-
}
|
|
1230
|
-
async function installEqlExtension(databaseUrl) {
|
|
1231
|
-
void databaseUrl;
|
|
1232
|
-
await delay(1500);
|
|
1233
|
-
}
|
|
1260
|
+
// src/bin/commands/init/steps/build-schema.ts
|
|
1261
|
+
import { existsSync as existsSync2, mkdirSync, writeFileSync } from "node:fs";
|
|
1262
|
+
import { dirname, resolve as resolve2 } from "node:path";
|
|
1234
1263
|
|
|
1235
1264
|
// src/bin/commands/init/types.ts
|
|
1236
1265
|
var CancelledError = class extends Error {
|
|
@@ -1239,104 +1268,343 @@ var CancelledError = class extends Error {
|
|
|
1239
1268
|
this.name = "CancelledError";
|
|
1240
1269
|
}
|
|
1241
1270
|
};
|
|
1271
|
+
function toIntegration(method) {
|
|
1272
|
+
switch (method) {
|
|
1273
|
+
case "drizzle":
|
|
1274
|
+
return "drizzle";
|
|
1275
|
+
case "supabase-js":
|
|
1276
|
+
return "supabase";
|
|
1277
|
+
case "prisma":
|
|
1278
|
+
case "raw-sql":
|
|
1279
|
+
return "postgresql";
|
|
1280
|
+
}
|
|
1281
|
+
}
|
|
1242
1282
|
|
|
1243
|
-
// src/bin/commands/init/
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1283
|
+
// src/bin/commands/init/utils.ts
|
|
1284
|
+
import { existsSync } from "node:fs";
|
|
1285
|
+
import { resolve } from "node:path";
|
|
1286
|
+
function isPackageInstalled(packageName) {
|
|
1287
|
+
const modulePath = resolve(process.cwd(), "node_modules", packageName);
|
|
1288
|
+
return existsSync(modulePath);
|
|
1289
|
+
}
|
|
1290
|
+
function detectPackageManager() {
|
|
1291
|
+
const cwd = process.cwd();
|
|
1292
|
+
if (existsSync(resolve(cwd, "bun.lockb")) || existsSync(resolve(cwd, "bun.lock")))
|
|
1293
|
+
return "bun";
|
|
1294
|
+
if (existsSync(resolve(cwd, "pnpm-lock.yaml"))) return "pnpm";
|
|
1295
|
+
if (existsSync(resolve(cwd, "yarn.lock"))) return "yarn";
|
|
1296
|
+
return "npm";
|
|
1297
|
+
}
|
|
1298
|
+
function devInstallCommand(pm, packageName) {
|
|
1299
|
+
switch (pm) {
|
|
1300
|
+
case "bun":
|
|
1301
|
+
return `bun add -D ${packageName}`;
|
|
1302
|
+
case "pnpm":
|
|
1303
|
+
return `pnpm add -D ${packageName}`;
|
|
1304
|
+
case "yarn":
|
|
1305
|
+
return `yarn add -D ${packageName}`;
|
|
1306
|
+
case "npm":
|
|
1307
|
+
return `npm install -D ${packageName}`;
|
|
1261
1308
|
}
|
|
1262
|
-
}
|
|
1309
|
+
}
|
|
1310
|
+
function toCamelCase(str) {
|
|
1311
|
+
return str.replace(/_([a-z])/g, (_3, c) => c.toUpperCase());
|
|
1312
|
+
}
|
|
1313
|
+
function drizzleTsType(dataType) {
|
|
1314
|
+
switch (dataType) {
|
|
1315
|
+
case "number":
|
|
1316
|
+
return "number";
|
|
1317
|
+
case "boolean":
|
|
1318
|
+
return "boolean";
|
|
1319
|
+
case "date":
|
|
1320
|
+
return "Date";
|
|
1321
|
+
case "json":
|
|
1322
|
+
return "Record<string, unknown>";
|
|
1323
|
+
default:
|
|
1324
|
+
return "string";
|
|
1325
|
+
}
|
|
1326
|
+
}
|
|
1327
|
+
function generateDrizzleFromSchema(schema) {
|
|
1328
|
+
const varName = `${toCamelCase(schema.tableName)}Table`;
|
|
1329
|
+
const schemaVarName = `${toCamelCase(schema.tableName)}Schema`;
|
|
1330
|
+
const columnDefs = schema.columns.map((col) => {
|
|
1331
|
+
const opts = [];
|
|
1332
|
+
if (col.dataType !== "string") {
|
|
1333
|
+
opts.push(`dataType: '${col.dataType}'`);
|
|
1334
|
+
}
|
|
1335
|
+
if (col.searchOps.includes("equality")) {
|
|
1336
|
+
opts.push("equality: true");
|
|
1337
|
+
}
|
|
1338
|
+
if (col.searchOps.includes("orderAndRange")) {
|
|
1339
|
+
opts.push("orderAndRange: true");
|
|
1340
|
+
}
|
|
1341
|
+
if (col.searchOps.includes("freeTextSearch")) {
|
|
1342
|
+
opts.push("freeTextSearch: true");
|
|
1343
|
+
}
|
|
1344
|
+
const tsType = drizzleTsType(col.dataType);
|
|
1345
|
+
const optsStr = opts.length > 0 ? `, {
|
|
1346
|
+
${opts.join(",\n ")},
|
|
1347
|
+
}` : "";
|
|
1348
|
+
return ` ${col.name}: encryptedType<${tsType}>('${col.name}'${optsStr}),`;
|
|
1349
|
+
});
|
|
1350
|
+
return `import { pgTable, integer, timestamp } from 'drizzle-orm/pg-core'
|
|
1351
|
+
import { encryptedType, extractEncryptionSchema } from '@cipherstash/stack/drizzle'
|
|
1352
|
+
import { Encryption } from '@cipherstash/stack'
|
|
1353
|
+
|
|
1354
|
+
export const ${varName} = pgTable('${schema.tableName}', {
|
|
1355
|
+
id: integer('id').primaryKey().generatedAlwaysAsIdentity(),
|
|
1356
|
+
${columnDefs.join("\n")}
|
|
1357
|
+
createdAt: timestamp('created_at').defaultNow(),
|
|
1358
|
+
})
|
|
1263
1359
|
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1360
|
+
const ${schemaVarName} = extractEncryptionSchema(${varName})
|
|
1361
|
+
|
|
1362
|
+
export const encryptionClient = await Encryption({
|
|
1363
|
+
schemas: [${schemaVarName}],
|
|
1364
|
+
})
|
|
1365
|
+
`;
|
|
1366
|
+
}
|
|
1367
|
+
function generateSchemaFromDef(schema) {
|
|
1368
|
+
const varName = `${toCamelCase(schema.tableName)}Table`;
|
|
1369
|
+
const columnDefs = schema.columns.map((col) => {
|
|
1370
|
+
const parts = [` ${col.name}: encryptedColumn('${col.name}')`];
|
|
1371
|
+
if (col.dataType !== "string") {
|
|
1372
|
+
parts.push(`.dataType('${col.dataType}')`);
|
|
1373
|
+
}
|
|
1374
|
+
for (const op of col.searchOps) {
|
|
1375
|
+
parts.push(`.${op}()`);
|
|
1376
|
+
}
|
|
1377
|
+
return `${parts.join("\n ")},`;
|
|
1378
|
+
});
|
|
1379
|
+
return `import { encryptedTable, encryptedColumn } from '@cipherstash/stack/schema'
|
|
1380
|
+
import { Encryption } from '@cipherstash/stack'
|
|
1381
|
+
|
|
1382
|
+
export const ${varName} = encryptedTable('${schema.tableName}', {
|
|
1383
|
+
${columnDefs.join("\n")}
|
|
1384
|
+
})
|
|
1385
|
+
|
|
1386
|
+
export const encryptionClient = await Encryption({
|
|
1387
|
+
schemas: [${varName}],
|
|
1388
|
+
})
|
|
1389
|
+
`;
|
|
1390
|
+
}
|
|
1391
|
+
function generateClientFromSchema(integration, schema) {
|
|
1392
|
+
switch (integration) {
|
|
1393
|
+
case "drizzle":
|
|
1394
|
+
return generateDrizzleFromSchema(schema);
|
|
1395
|
+
case "supabase":
|
|
1396
|
+
case "postgresql":
|
|
1397
|
+
return generateSchemaFromDef(schema);
|
|
1398
|
+
}
|
|
1399
|
+
}
|
|
1400
|
+
function generatePlaceholderClient(integration) {
|
|
1401
|
+
const placeholder = {
|
|
1402
|
+
tableName: "users",
|
|
1403
|
+
columns: [
|
|
1404
|
+
{
|
|
1405
|
+
name: "email",
|
|
1406
|
+
dataType: "string",
|
|
1407
|
+
searchOps: ["equality", "freeTextSearch"]
|
|
1408
|
+
},
|
|
1409
|
+
{
|
|
1410
|
+
name: "name",
|
|
1411
|
+
dataType: "string",
|
|
1412
|
+
searchOps: ["equality", "freeTextSearch"]
|
|
1413
|
+
}
|
|
1414
|
+
]
|
|
1415
|
+
};
|
|
1416
|
+
return generateClientFromSchema(integration, placeholder);
|
|
1417
|
+
}
|
|
1418
|
+
|
|
1419
|
+
// src/bin/commands/init/steps/build-schema.ts
|
|
1420
|
+
var DEFAULT_CLIENT_PATH = "./src/encryption/index.ts";
|
|
1421
|
+
async function addColumn(index) {
|
|
1422
|
+
const name = await he({
|
|
1423
|
+
message: `Column ${index} name:`,
|
|
1424
|
+
placeholder: index === 1 ? "email" : "name",
|
|
1425
|
+
validate(value) {
|
|
1426
|
+
if (!value || value.trim().length === 0) {
|
|
1427
|
+
return "Column name is required.";
|
|
1428
|
+
}
|
|
1429
|
+
if (!/^[a-z_][a-z0-9_]*$/i.test(value)) {
|
|
1430
|
+
return "Column name must be a valid identifier.";
|
|
1431
|
+
}
|
|
1432
|
+
}
|
|
1433
|
+
});
|
|
1434
|
+
if (pD(name)) return void 0;
|
|
1435
|
+
const dataType = await ve({
|
|
1436
|
+
message: `Data type for "${name}":`,
|
|
1437
|
+
options: [
|
|
1438
|
+
{ value: "string", label: "string", hint: "text, email, name, etc." },
|
|
1439
|
+
{ value: "number", label: "number", hint: "integer or decimal" },
|
|
1440
|
+
{ value: "boolean", label: "boolean" },
|
|
1441
|
+
{ value: "date", label: "date", hint: "Date object" },
|
|
1442
|
+
{ value: "json", label: "json", hint: "structured JSON data" }
|
|
1443
|
+
]
|
|
1444
|
+
});
|
|
1445
|
+
if (pD(dataType)) return void 0;
|
|
1446
|
+
const searchOptions = [
|
|
1447
|
+
{ value: "equality", label: "Exact match", hint: "eq, neq, in" },
|
|
1448
|
+
{
|
|
1449
|
+
value: "orderAndRange",
|
|
1450
|
+
label: "Order and range",
|
|
1451
|
+
hint: "gt, gte, lt, lte, between, sorting"
|
|
1452
|
+
}
|
|
1453
|
+
];
|
|
1454
|
+
if (dataType === "string") {
|
|
1455
|
+
searchOptions.push({
|
|
1456
|
+
value: "freeTextSearch",
|
|
1457
|
+
label: "Free-text search",
|
|
1458
|
+
hint: "like, ilike, substring matching"
|
|
1459
|
+
});
|
|
1460
|
+
}
|
|
1461
|
+
const searchOps = await fe({
|
|
1462
|
+
message: `Search operations for "${name}":`,
|
|
1463
|
+
options: searchOptions,
|
|
1464
|
+
required: false
|
|
1465
|
+
});
|
|
1466
|
+
if (pD(searchOps)) return void 0;
|
|
1467
|
+
return { name, dataType, searchOps };
|
|
1267
1468
|
}
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1469
|
+
async function buildSchema() {
|
|
1470
|
+
const tableName = await he({
|
|
1471
|
+
message: "What is the name of your table?",
|
|
1472
|
+
placeholder: "users",
|
|
1473
|
+
validate(value) {
|
|
1474
|
+
if (!value || value.trim().length === 0) {
|
|
1475
|
+
return "Table name is required.";
|
|
1476
|
+
}
|
|
1477
|
+
if (!/^[a-z_][a-z0-9_]*$/i.test(value)) {
|
|
1478
|
+
return "Table name must be a valid identifier (letters, numbers, underscores).";
|
|
1479
|
+
}
|
|
1480
|
+
}
|
|
1481
|
+
});
|
|
1482
|
+
if (pD(tableName)) return void 0;
|
|
1483
|
+
const columns = [];
|
|
1484
|
+
M2.info("Add encrypted columns to your table. You can add more later.");
|
|
1485
|
+
while (true) {
|
|
1486
|
+
const column = await addColumn(columns.length + 1);
|
|
1487
|
+
if (!column) return void 0;
|
|
1488
|
+
columns.push(column);
|
|
1489
|
+
const addMore = await ye({
|
|
1490
|
+
message: "Add another encrypted column?",
|
|
1491
|
+
initialValue: false
|
|
1492
|
+
});
|
|
1493
|
+
if (pD(addMore)) return void 0;
|
|
1494
|
+
if (!addMore) break;
|
|
1495
|
+
}
|
|
1496
|
+
M2.success(
|
|
1497
|
+
`Schema defined: ${tableName} with ${columns.length} encrypted column${columns.length !== 1 ? "s" : ""}`
|
|
1498
|
+
);
|
|
1499
|
+
return { tableName, columns };
|
|
1500
|
+
}
|
|
1501
|
+
var buildSchemaStep = {
|
|
1502
|
+
id: "build-schema",
|
|
1503
|
+
name: "Build encryption schema",
|
|
1271
1504
|
async run(state, _provider) {
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
${maskUrl(envUrl)}`);
|
|
1276
|
-
return { ...state, databaseUrl: envUrl };
|
|
1505
|
+
if (!state.connectionMethod) {
|
|
1506
|
+
M2.warn("Skipping schema generation (no connection method selected)");
|
|
1507
|
+
return { ...state, schemaGenerated: false };
|
|
1277
1508
|
}
|
|
1278
|
-
|
|
1279
|
-
const
|
|
1280
|
-
message: "
|
|
1509
|
+
const integration = toIntegration(state.connectionMethod);
|
|
1510
|
+
const clientFilePath = await he({
|
|
1511
|
+
message: "Where should we create your encryption client?",
|
|
1512
|
+
placeholder: DEFAULT_CLIENT_PATH,
|
|
1513
|
+
defaultValue: DEFAULT_CLIENT_PATH
|
|
1514
|
+
});
|
|
1515
|
+
if (pD(clientFilePath)) throw new CancelledError();
|
|
1516
|
+
const resolvedPath = resolve2(process.cwd(), clientFilePath);
|
|
1517
|
+
if (existsSync2(resolvedPath)) {
|
|
1518
|
+
const action = await ve({
|
|
1519
|
+
message: `${clientFilePath} already exists. What would you like to do?`,
|
|
1520
|
+
options: [
|
|
1521
|
+
{
|
|
1522
|
+
value: "keep",
|
|
1523
|
+
label: "Keep existing file",
|
|
1524
|
+
hint: "skip code generation"
|
|
1525
|
+
},
|
|
1526
|
+
{ value: "overwrite", label: "Overwrite with new schema" }
|
|
1527
|
+
]
|
|
1528
|
+
});
|
|
1529
|
+
if (pD(action)) throw new CancelledError();
|
|
1530
|
+
if (action === "keep") {
|
|
1531
|
+
M2.info("Keeping existing encryption client file.");
|
|
1532
|
+
return { ...state, clientFilePath, schemaGenerated: false };
|
|
1533
|
+
}
|
|
1534
|
+
}
|
|
1535
|
+
const schemaChoice = await ve({
|
|
1536
|
+
message: "How would you like to set up your encryption schema?",
|
|
1281
1537
|
options: [
|
|
1282
|
-
{
|
|
1283
|
-
|
|
1538
|
+
{
|
|
1539
|
+
value: "build",
|
|
1540
|
+
label: "Build schema now",
|
|
1541
|
+
hint: "interactive wizard"
|
|
1542
|
+
},
|
|
1543
|
+
{
|
|
1544
|
+
value: "placeholder",
|
|
1545
|
+
label: "Use placeholder schema",
|
|
1546
|
+
hint: "edit later"
|
|
1547
|
+
}
|
|
1284
1548
|
]
|
|
1285
1549
|
});
|
|
1286
|
-
if (pD(
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
return "Must be a PostgreSQL connection string";
|
|
1295
|
-
}
|
|
1296
|
-
});
|
|
1297
|
-
if (pD(url)) throw new CancelledError();
|
|
1298
|
-
return { ...state, databaseUrl: url };
|
|
1550
|
+
if (pD(schemaChoice)) throw new CancelledError();
|
|
1551
|
+
let fileContents;
|
|
1552
|
+
if (schemaChoice === "build") {
|
|
1553
|
+
const schema = await buildSchema();
|
|
1554
|
+
if (!schema) throw new CancelledError();
|
|
1555
|
+
fileContents = generateClientFromSchema(integration, schema);
|
|
1556
|
+
} else {
|
|
1557
|
+
fileContents = generatePlaceholderClient(integration);
|
|
1299
1558
|
}
|
|
1300
|
-
|
|
1559
|
+
const dir = dirname(resolvedPath);
|
|
1560
|
+
if (!existsSync2(dir)) {
|
|
1561
|
+
mkdirSync(dir, { recursive: true });
|
|
1562
|
+
}
|
|
1563
|
+
writeFileSync(resolvedPath, fileContents, "utf-8");
|
|
1564
|
+
M2.success(`Encryption client written to ${clientFilePath}`);
|
|
1565
|
+
return { ...state, clientFilePath, schemaGenerated: true };
|
|
1301
1566
|
}
|
|
1302
1567
|
};
|
|
1303
1568
|
|
|
1304
|
-
// src/bin/commands/init/steps/install-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1569
|
+
// src/bin/commands/init/steps/install-forge.ts
|
|
1570
|
+
import { execSync } from "node:child_process";
|
|
1571
|
+
var FORGE_PACKAGE = "@cipherstash/stack-forge";
|
|
1572
|
+
var installForgeStep = {
|
|
1573
|
+
id: "install-forge",
|
|
1574
|
+
name: "Install stack-forge",
|
|
1308
1575
|
async run(state, _provider) {
|
|
1309
|
-
if (
|
|
1310
|
-
M2.
|
|
1311
|
-
return { ...state,
|
|
1576
|
+
if (isPackageInstalled(FORGE_PACKAGE)) {
|
|
1577
|
+
M2.success(`${FORGE_PACKAGE} is already installed.`);
|
|
1578
|
+
return { ...state, forgeInstalled: true };
|
|
1312
1579
|
}
|
|
1580
|
+
const pm = detectPackageManager();
|
|
1581
|
+
const cmd = devInstallCommand(pm, FORGE_PACKAGE);
|
|
1313
1582
|
const install = await ye({
|
|
1314
|
-
message:
|
|
1583
|
+
message: `Install ${FORGE_PACKAGE} as a dev dependency? (${cmd})`
|
|
1315
1584
|
});
|
|
1316
1585
|
if (pD(install)) throw new CancelledError();
|
|
1317
1586
|
if (!install) {
|
|
1318
|
-
M2.info(
|
|
1587
|
+
M2.info(`Skipping ${FORGE_PACKAGE} installation.`);
|
|
1319
1588
|
Me(
|
|
1320
|
-
|
|
1589
|
+
`You can install it manually later:
|
|
1590
|
+
${cmd}`,
|
|
1321
1591
|
"Manual Installation"
|
|
1322
1592
|
);
|
|
1323
|
-
return { ...state,
|
|
1593
|
+
return { ...state, forgeInstalled: false };
|
|
1324
1594
|
}
|
|
1325
1595
|
const s = Y2();
|
|
1326
|
-
s.start(
|
|
1596
|
+
s.start(`Installing ${FORGE_PACKAGE}...`);
|
|
1327
1597
|
try {
|
|
1328
|
-
|
|
1329
|
-
s.stop(
|
|
1330
|
-
return { ...state,
|
|
1598
|
+
execSync(cmd, { cwd: process.cwd(), stdio: "pipe" });
|
|
1599
|
+
s.stop(`${FORGE_PACKAGE} installed successfully`);
|
|
1600
|
+
return { ...state, forgeInstalled: true };
|
|
1331
1601
|
} catch (err) {
|
|
1332
1602
|
const message = err instanceof Error ? err.message : String(err);
|
|
1333
|
-
s.stop(
|
|
1603
|
+
s.stop(`${FORGE_PACKAGE} installation failed`);
|
|
1334
1604
|
M2.error(message);
|
|
1335
|
-
Me(
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
);
|
|
1339
|
-
return { ...state, eqlInstalled: false };
|
|
1605
|
+
Me(`You can install it manually:
|
|
1606
|
+
${cmd}`, "Manual Installation");
|
|
1607
|
+
return { ...state, forgeInstalled: false };
|
|
1340
1608
|
}
|
|
1341
1609
|
}
|
|
1342
1610
|
};
|
|
@@ -1367,75 +1635,14 @@ var selectConnectionStep = {
|
|
|
1367
1635
|
}
|
|
1368
1636
|
};
|
|
1369
1637
|
|
|
1370
|
-
// src/bin/commands/init/steps/select-region.ts
|
|
1371
|
-
var REGIONS = [
|
|
1372
|
-
{ value: "ap-southeast-2", label: "Asia Pacific (Sydney)" },
|
|
1373
|
-
{ value: "us-east-1", label: "US East (N. Virginia)" },
|
|
1374
|
-
{ value: "us-west-2", label: "US West (Oregon)" },
|
|
1375
|
-
{ value: "eu-west-1", label: "Europe (Ireland)" }
|
|
1376
|
-
];
|
|
1377
|
-
var selectRegionStep = {
|
|
1378
|
-
id: "select-region",
|
|
1379
|
-
name: "Select region",
|
|
1380
|
-
async run(state, _provider) {
|
|
1381
|
-
const region = await ve({
|
|
1382
|
-
message: "Where should we create your workspace?",
|
|
1383
|
-
options: REGIONS
|
|
1384
|
-
});
|
|
1385
|
-
if (pD(region)) throw new CancelledError();
|
|
1386
|
-
return { ...state, region };
|
|
1387
|
-
}
|
|
1388
|
-
};
|
|
1389
|
-
|
|
1390
|
-
// src/bin/commands/init/steps/select-workspace.ts
|
|
1391
|
-
var selectWorkspaceStep = {
|
|
1392
|
-
id: "select-workspace",
|
|
1393
|
-
name: "Select workspace",
|
|
1394
|
-
async run(state, _provider) {
|
|
1395
|
-
const s = Y2();
|
|
1396
|
-
s.start("Loading workspaces...");
|
|
1397
|
-
const workspaces = await fetchWorkspaces(state.accessToken);
|
|
1398
|
-
s.stop("Workspaces loaded");
|
|
1399
|
-
const options = [
|
|
1400
|
-
...workspaces.map((ws2) => ({ value: ws2.id, label: ws2.name })),
|
|
1401
|
-
{ value: "__create__", label: "Create new workspace" }
|
|
1402
|
-
];
|
|
1403
|
-
const selected = await ve({
|
|
1404
|
-
message: "Select a workspace",
|
|
1405
|
-
options
|
|
1406
|
-
});
|
|
1407
|
-
if (pD(selected)) throw new CancelledError();
|
|
1408
|
-
if (selected === "__create__") {
|
|
1409
|
-
const name = await he({
|
|
1410
|
-
message: "What should we call your new workspace?",
|
|
1411
|
-
placeholder: "my-project",
|
|
1412
|
-
validate: (val) => {
|
|
1413
|
-
if (!val.trim()) return "Workspace name is required";
|
|
1414
|
-
}
|
|
1415
|
-
});
|
|
1416
|
-
if (pD(name)) throw new CancelledError();
|
|
1417
|
-
const s2 = Y2();
|
|
1418
|
-
s2.start("Creating workspace...");
|
|
1419
|
-
const ws2 = await createWorkspace(state.accessToken, name);
|
|
1420
|
-
s2.stop(`Workspace created: ${ws2.name}`);
|
|
1421
|
-
return { ...state, workspaceId: ws2.id, workspaceName: ws2.name };
|
|
1422
|
-
}
|
|
1423
|
-
const ws = workspaces.find((w2) => w2.id === selected);
|
|
1424
|
-
return { ...state, workspaceId: ws.id, workspaceName: ws.name };
|
|
1425
|
-
}
|
|
1426
|
-
};
|
|
1427
|
-
|
|
1428
1638
|
// src/bin/commands/init/index.ts
|
|
1429
1639
|
var PROVIDER_MAP = {
|
|
1430
1640
|
supabase: createSupabaseProvider
|
|
1431
1641
|
};
|
|
1432
1642
|
var STEPS = [
|
|
1433
|
-
authenticateStep,
|
|
1434
|
-
selectWorkspaceStep,
|
|
1435
|
-
selectRegionStep,
|
|
1436
1643
|
selectConnectionStep,
|
|
1437
|
-
|
|
1438
|
-
|
|
1644
|
+
buildSchemaStep,
|
|
1645
|
+
installForgeStep,
|
|
1439
1646
|
nextStepsStep
|
|
1440
1647
|
];
|
|
1441
1648
|
function resolveProvider(flags) {
|
|
@@ -1449,9 +1656,6 @@ function resolveProvider(flags) {
|
|
|
1449
1656
|
async function initCommand(flags) {
|
|
1450
1657
|
const provider = resolveProvider(flags);
|
|
1451
1658
|
Ie("CipherStash Stack Setup");
|
|
1452
|
-
M2.warn(
|
|
1453
|
-
"This command is a prototype and a sneak peek at what's next. It doesn't perform any actual setup yet."
|
|
1454
|
-
);
|
|
1455
1659
|
M2.info(provider.introMessage);
|
|
1456
1660
|
let state = {};
|
|
1457
1661
|
try {
|
|
@@ -1511,6 +1715,17 @@ function isEncryptedPayload(value) {
|
|
|
1511
1715
|
|
|
1512
1716
|
// src/schema/index.ts
|
|
1513
1717
|
import { z as z2 } from "zod";
|
|
1718
|
+
var eqlCastAsEnum = z2.enum([
|
|
1719
|
+
"text",
|
|
1720
|
+
"int",
|
|
1721
|
+
"small_int",
|
|
1722
|
+
"big_int",
|
|
1723
|
+
"real",
|
|
1724
|
+
"double",
|
|
1725
|
+
"boolean",
|
|
1726
|
+
"date",
|
|
1727
|
+
"jsonb"
|
|
1728
|
+
]).default("text");
|
|
1514
1729
|
var castAsEnum = z2.enum(["bigint", "boolean", "date", "number", "string", "json", "text"]).default("text");
|
|
1515
1730
|
var tokenFilterSchema = z2.object({
|
|
1516
1731
|
kind: z2.literal("downcase")
|
|
@@ -1559,7 +1774,7 @@ var EncryptedField = class {
|
|
|
1559
1774
|
castAsValue;
|
|
1560
1775
|
constructor(valueName) {
|
|
1561
1776
|
this.valueName = valueName;
|
|
1562
|
-
this.castAsValue = "
|
|
1777
|
+
this.castAsValue = "string";
|
|
1563
1778
|
}
|
|
1564
1779
|
/**
|
|
1565
1780
|
* Set or override the plaintext data type for this field.
|
|
@@ -1584,7 +1799,7 @@ var EncryptedField = class {
|
|
|
1584
1799
|
}
|
|
1585
1800
|
build() {
|
|
1586
1801
|
return {
|
|
1587
|
-
cast_as: this.castAsValue
|
|
1802
|
+
cast_as: this.castAsValue,
|
|
1588
1803
|
indexes: {}
|
|
1589
1804
|
};
|
|
1590
1805
|
}
|
|
@@ -1738,7 +1953,7 @@ var EncryptedColumn = class {
|
|
|
1738
1953
|
}
|
|
1739
1954
|
build() {
|
|
1740
1955
|
return {
|
|
1741
|
-
cast_as: this.castAsValue
|
|
1956
|
+
cast_as: this.castAsValue,
|
|
1742
1957
|
indexes: this.indexesValue
|
|
1743
1958
|
};
|
|
1744
1959
|
}
|
|
@@ -4955,7 +5170,7 @@ async function secretsCommand(args) {
|
|
|
4955
5170
|
|
|
4956
5171
|
// src/bin/stash.ts
|
|
4957
5172
|
(0, import_dotenv2.config)();
|
|
4958
|
-
var __dirname =
|
|
5173
|
+
var __dirname = dirname2(fileURLToPath(import.meta.url));
|
|
4959
5174
|
var pkg = JSON.parse(
|
|
4960
5175
|
readFileSync(join(__dirname, "../../package.json"), "utf-8")
|
|
4961
5176
|
);
|