Skip to content

Commit 4296eb1

Browse files
authored
Merge pull request #336 from cipherstash/cli
feat: consolidate cli tools into @cipherstash/cli
2 parents abf3ddd + 8ebc77b commit 4296eb1

File tree

93 files changed

+6764
-1348
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

93 files changed

+6764
-1348
lines changed

examples/basic/index.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import 'dotenv/config'
22
import readline from 'node:readline'
33
import { client, users } from './encrypt'
4+
import { getAllContacts, createContact } from './src/queries/contacts'
45

56
const rl = readline.createInterface({
67
input: process.stdin,
@@ -68,6 +69,31 @@ async function main() {
6869

6970
console.log('Bulk encrypted data:', bulkEncryptResult.data)
7071

72+
// Demonstrate Supabase integration with CipherStash encryption
73+
console.log('\n--- Supabase Integration Demo ---')
74+
75+
try {
76+
// Example: Create a new contact (would insert into encrypted Supabase table)
77+
console.log('Creating encrypted contact...')
78+
const newContact = {
79+
name: 'John Doe',
80+
email: 'john@example.com',
81+
role: 'Developer' // This field will be encrypted using CipherStash
82+
}
83+
84+
// Note: This would fail in this basic example since we don't have actual Supabase setup
85+
// but shows the pattern for encrypted Supabase usage
86+
console.log('Contact data to encrypt:', newContact)
87+
88+
// Example: Fetch contacts (would decrypt results from Supabase)
89+
console.log('Fetching encrypted contacts...')
90+
// const contacts = await getAllContacts()
91+
// console.log('Decrypted contacts:', contacts.data)
92+
93+
} catch (error) {
94+
console.log('Supabase demo skipped (no actual Supabase connection in this basic example)')
95+
}
96+
7197
rl.close()
7298
}
7399

examples/basic/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
"pg": "8.13.1"
1717
},
1818
"devDependencies": {
19-
"@cipherstash/stack-forge": "workspace:*",
19+
"@cipherstash/cli": "workspace:*",
2020
"tsx": "catalog:repo",
2121
"typescript": "catalog:repo"
2222
}
Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,22 @@
1-
import { encryptedTable, encryptedColumn } from '@cipherstash/stack/schema'
1+
import { pgTable, integer, timestamp } from 'drizzle-orm/pg-core'
2+
import { encryptedType, extractEncryptionSchema } from '@cipherstash/stack/drizzle'
23
import { Encryption } from '@cipherstash/stack'
34

4-
export const usersTable = encryptedTable('users', {
5-
email: encryptedColumn('email')
6-
.equality()
7-
.orderAndRange()
8-
.freeTextSearch(),
5+
export const usersTable = pgTable('users', {
6+
id: integer('id').primaryKey().generatedAlwaysAsIdentity(),
7+
email: encryptedType<string>('email', {
8+
equality: true,
9+
freeTextSearch: true,
10+
}),
11+
name: encryptedType<string>('name', {
12+
equality: true,
13+
freeTextSearch: true,
14+
}),
15+
createdAt: timestamp('created_at').defaultNow(),
916
})
1017

18+
const usersSchema = extractEncryptionSchema(usersTable)
19+
1120
export const encryptionClient = await Encryption({
12-
schemas: [usersTable],
21+
schemas: [usersSchema],
1322
})
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { encryptedSupabase } from '@cipherstash/stack/supabase'
2+
import { encryptionClient, contactsTable } from '../../encryption/index'
3+
import { createServerClient } from './server'
4+
5+
const supabase = await createServerClient()
6+
export const eSupabase = encryptedSupabase({
7+
encryptionClient,
8+
supabaseClient: supabase,
9+
})
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import { createClient } from '@supabase/supabase-js'
2+
3+
export async function createServerClient() {
4+
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!
5+
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
6+
7+
return createClient(supabaseUrl, supabaseKey)
8+
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import { eSupabase } from '../lib/supabase/encrypted'
2+
import { contactsTable } from '../encryption/index'
3+
4+
// Example queries using encrypted Supabase wrapper
5+
6+
export async function getAllContacts() {
7+
const { data, error } = await eSupabase
8+
.from('contacts', contactsTable)
9+
.select('id, name, email, role') // explicit columns, no *
10+
.order('created_at', { ascending: false })
11+
12+
return { data, error }
13+
}
14+
15+
export async function getContactsByRole(role: string) {
16+
const { data, error } = await eSupabase
17+
.from('contacts', contactsTable)
18+
.select('id, name, email, role')
19+
.eq('role', role) // auto-encrypted
20+
21+
return { data, error }
22+
}
23+
24+
export async function searchContactsByName(searchTerm: string) {
25+
const { data, error } = await eSupabase
26+
.from('contacts', contactsTable)
27+
.select('id, name, email, role')
28+
.ilike('name', `%${searchTerm}%`) // auto-encrypted
29+
30+
return { data, error }
31+
}
32+
33+
export async function createContact(contact: { name: string; email: string; role: string }) {
34+
const { data, error } = await eSupabase
35+
.from('contacts', contactsTable)
36+
.insert(contact) // auto-encrypted
37+
.select('id, name, email, role')
38+
.single()
39+
40+
return { data, error }
41+
}
42+
43+
export async function updateContact(id: string, updates: Partial<{ name: string; email: string; role: string }>) {
44+
const { data, error } = await eSupabase
45+
.from('contacts', contactsTable)
46+
.update(updates) // auto-encrypted
47+
.eq('id', id)
48+
.select('id, name, email, role')
49+
.single()
50+
51+
return { data, error }
52+
}
53+
54+
export async function deleteContact(id: string) {
55+
const { error } = await eSupabase
56+
.from('contacts', contactsTable)
57+
.delete()
58+
.eq('id', id)
59+
60+
return { error }
61+
}

examples/basic/stash.config.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { defineConfig } from '@cipherstash/stack-forge'
1+
import { defineConfig } from '@cipherstash/cli'
22

33
export default defineConfig({
44
databaseUrl: process.env.DATABASE_URL!,

package.json

Lines changed: 42 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,26 @@
1818
"url": "git+https://github.com/cipherstash/protectjs.git"
1919
},
2020
"license": "MIT",
21-
"workspaces": [
22-
"examples/*",
23-
"packages/*"
24-
],
21+
"workspaces": {
22+
"packages": [
23+
"packages/*",
24+
"examples/*"
25+
],
26+
"catalogs": {
27+
"repo": {
28+
"@cipherstash/auth": "0.35.0",
29+
"tsup": "8.4.0",
30+
"tsx": "4.19.3",
31+
"typescript": "5.6.3",
32+
"vitest": "3.1.3"
33+
},
34+
"security": {
35+
"@clerk/nextjs": "6.31.2",
36+
"next": "15.5.10",
37+
"vite": "6.4.1"
38+
}
39+
}
40+
},
2541
"scripts": {
2642
"build": "turbo build --filter './packages/*'",
2743
"build:js": "turbo build --filter './packages/protect' --filter './packages/nextjs'",
@@ -46,29 +62,6 @@
4662
"node": ">=22"
4763
},
4864
"pnpm": {
49-
"overrides": {
50-
"@cipherstash/protect-ffi": "0.21.0",
51-
"@babel/runtime": "7.26.10",
52-
"brace-expansion@^5": ">=5.0.5",
53-
"body-parser": "2.2.1",
54-
"vite": "catalog:security",
55-
"pg": "^8.16.3",
56-
"postgres": "^3.4.7",
57-
"js-yaml": "3.14.2",
58-
"test-exclude": "^7.0.1",
59-
"glob": ">=11.1.0",
60-
"qs": ">=6.14.1",
61-
"lodash": ">=4.17.23",
62-
"minimatch": ">=10.2.3",
63-
"@isaacs/brace-expansion": ">=5.0.1",
64-
"fast-xml-parser": ">=5.3.4",
65-
"next": ">=15.5.10",
66-
"ajv": ">=8.18.0",
67-
"esbuild@<=0.24.2": ">=0.25.0",
68-
"picomatch@^4": ">=4.0.4",
69-
"picomatch@^2": ">=2.3.2",
70-
"rollup@>=4.0.0 <4.59.0": ">=4.59.0"
71-
},
7265
"peerDependencyRules": {
7366
"ignoreMissing": [
7467
"@types/pg",
@@ -80,5 +73,27 @@
8073
}
8174
},
8275
"dedupe-peer-dependents": true
76+
},
77+
"overrides": {
78+
"@babel/runtime": "7.26.10",
79+
"brace-expansion@^5": ">=5.0.5",
80+
"body-parser": "2.2.1",
81+
"vite": "catalog:security",
82+
"pg": "^8.16.3",
83+
"postgres": "^3.4.7",
84+
"js-yaml": "3.14.2",
85+
"test-exclude": "^7.0.1",
86+
"glob": ">=11.1.0",
87+
"qs": ">=6.14.1",
88+
"lodash": ">=4.17.23",
89+
"minimatch": ">=10.2.3",
90+
"@isaacs/brace-expansion": ">=5.0.1",
91+
"fast-xml-parser": ">=5.3.4",
92+
"next": ">=15.5.10",
93+
"ajv": ">=8.18.0",
94+
"esbuild@<=0.24.2": ">=0.25.0",
95+
"picomatch@^4": ">=4.0.4",
96+
"picomatch@^2": ">=2.3.2",
97+
"rollup@>=4.0.0 <4.59.0": ">=4.59.0"
8398
}
8499
}
Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
# @cipherstash/stack-forge
1+
# @cipherstash/cli
2+
3+
> Renamed from `@cipherstash/stack-forge`. The standalone `@cipherstash/wizard` package was absorbed into this CLI as `npx @cipherstash/cli wizard`. The single binary is now invoked via `npx @cipherstash/cli` (replaces `stash-forge` and `cipherstash-wizard`).
24
35
## 0.4.0
46

0 commit comments

Comments
 (0)