Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions examples/basic/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import 'dotenv/config'
import readline from 'node:readline'
import { client, users } from './encrypt'
import { getAllContacts, createContact } from './src/queries/contacts'

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

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

// Demonstrate Supabase integration with CipherStash encryption
console.log('\n--- Supabase Integration Demo ---')

try {
// Example: Create a new contact (would insert into encrypted Supabase table)
console.log('Creating encrypted contact...')
const newContact = {
name: 'John Doe',
email: 'john@example.com',
role: 'Developer' // This field will be encrypted using CipherStash
}

// Note: This would fail in this basic example since we don't have actual Supabase setup
// but shows the pattern for encrypted Supabase usage
console.log('Contact data to encrypt:', newContact)

// Example: Fetch contacts (would decrypt results from Supabase)
console.log('Fetching encrypted contacts...')
// const contacts = await getAllContacts()
// console.log('Decrypted contacts:', contacts.data)

} catch (error) {
console.log('Supabase demo skipped (no actual Supabase connection in this basic example)')
}

rl.close()
}

Expand Down
2 changes: 1 addition & 1 deletion examples/basic/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
"pg": "8.13.1"
},
"devDependencies": {
"@cipherstash/stack-forge": "workspace:*",
"@cipherstash/cli": "workspace:*",
"tsx": "catalog:repo",
"typescript": "catalog:repo"
}
Expand Down
23 changes: 16 additions & 7 deletions examples/basic/src/encryption/index.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,22 @@
import { encryptedTable, encryptedColumn } from '@cipherstash/stack/schema'
import { pgTable, integer, timestamp } from 'drizzle-orm/pg-core'
import { encryptedType, extractEncryptionSchema } from '@cipherstash/stack/drizzle'
import { Encryption } from '@cipherstash/stack'

export const usersTable = encryptedTable('users', {
email: encryptedColumn('email')
.equality()
.orderAndRange()
.freeTextSearch(),
export const usersTable = pgTable('users', {
id: integer('id').primaryKey().generatedAlwaysAsIdentity(),
email: encryptedType<string>('email', {
equality: true,
freeTextSearch: true,
}),
name: encryptedType<string>('name', {
equality: true,
freeTextSearch: true,
}),
createdAt: timestamp('created_at').defaultNow(),
})

const usersSchema = extractEncryptionSchema(usersTable)

export const encryptionClient = await Encryption({
schemas: [usersTable],
schemas: [usersSchema],
})
9 changes: 9 additions & 0 deletions examples/basic/src/lib/supabase/encrypted.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { encryptedSupabase } from '@cipherstash/stack/supabase'
import { encryptionClient, contactsTable } from '../../encryption/index'
import { createServerClient } from './server'

const supabase = await createServerClient()
export const eSupabase = encryptedSupabase({
encryptionClient,
supabaseClient: supabase,
})
8 changes: 8 additions & 0 deletions examples/basic/src/lib/supabase/server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { createClient } from '@supabase/supabase-js'

export async function createServerClient() {
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!

return createClient(supabaseUrl, supabaseKey)
}
61 changes: 61 additions & 0 deletions examples/basic/src/queries/contacts.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { eSupabase } from '../lib/supabase/encrypted'
import { contactsTable } from '../encryption/index'

// Example queries using encrypted Supabase wrapper

export async function getAllContacts() {
const { data, error } = await eSupabase
.from('contacts', contactsTable)
.select('id, name, email, role') // explicit columns, no *
.order('created_at', { ascending: false })

return { data, error }
}

export async function getContactsByRole(role: string) {
const { data, error } = await eSupabase
.from('contacts', contactsTable)
.select('id, name, email, role')
.eq('role', role) // auto-encrypted

return { data, error }
}

export async function searchContactsByName(searchTerm: string) {
const { data, error } = await eSupabase
.from('contacts', contactsTable)
.select('id, name, email, role')
.ilike('name', `%${searchTerm}%`) // auto-encrypted

return { data, error }
}

export async function createContact(contact: { name: string; email: string; role: string }) {
const { data, error } = await eSupabase
.from('contacts', contactsTable)
.insert(contact) // auto-encrypted
.select('id, name, email, role')
.single()

return { data, error }
}

export async function updateContact(id: string, updates: Partial<{ name: string; email: string; role: string }>) {
const { data, error } = await eSupabase
.from('contacts', contactsTable)
.update(updates) // auto-encrypted
.eq('id', id)
.select('id, name, email, role')
.single()

return { data, error }
}

export async function deleteContact(id: string) {
const { error } = await eSupabase
.from('contacts', contactsTable)
.delete()
.eq('id', id)

return { error }
}
2 changes: 1 addition & 1 deletion examples/basic/stash.config.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { defineConfig } from '@cipherstash/stack-forge'
import { defineConfig } from '@cipherstash/cli'

export default defineConfig({
databaseUrl: process.env.DATABASE_URL!,
Expand Down
69 changes: 42 additions & 27 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,26 @@
"url": "git+https://github.com/cipherstash/protectjs.git"
},
"license": "MIT",
"workspaces": [
"examples/*",
"packages/*"
],
"workspaces": {
"packages": [
"packages/*",
"examples/*"
],
"catalogs": {
"repo": {
"@cipherstash/auth": "0.35.0",
"tsup": "8.4.0",
"tsx": "4.19.3",
"typescript": "5.6.3",
"vitest": "3.1.3"
},
"security": {
"@clerk/nextjs": "6.31.2",
"next": "15.5.10",
"vite": "6.4.1"
}
}
},
Comment on lines +21 to +40
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify if pnpm uses workspaces.catalogs from package.json or only from pnpm-workspace.yaml

# Check pnpm-workspace.yaml for catalog definitions
echo "=== pnpm-workspace.yaml catalogs ==="
cat pnpm-workspace.yaml

# Check if any documentation or config references this pattern
echo -e "\n=== Searching for workspaces.catalogs usage pattern ==="
rg -l "workspaces.*catalogs" --type json

Repository: cipherstash/stack

Length of output: 391


🌐 Web query:

Does pnpm support catalogs in package.json workspaces field?

💡 Result:

No. pnpm catalogs are not read from package.json#workspaces.

In pnpm, catalogs are defined in pnpm-workspace.yaml (via catalog: / catalogs:) and then referenced from individual packages’ package.json files using the catalog: protocol in dependency specifiers. (pnpm.cn)

If you’ve seen catalogs under package.json#workspaces, that’s a Bun workspace feature/documentation pattern, not pnpm’s. (fossies.org)

Related: pnpm has historically required pnpm-workspace.yaml rather than using package.json#workspaces (there’s even a discussion proposing the opposite, which implies it’s not how pnpm works today). (github.com)

Citations:


Remove the workspaces.catalogs object from package.json—it's dead code in a pnpm project.

The workspaces field with catalogs is a Bun workspace feature. pnpm does not read or use package.json#workspaces.catalogs. Instead, pnpm exclusively reads workspace and catalog configuration from pnpm-workspace.yaml. The catalog definitions in package.json (lines 30–40) duplicate those in pnpm-workspace.yaml and will never be used, creating maintenance burden and potential for version drift between the two files.

Since this project uses pnpm, move any needed catalog reference to pnpm-workspace.yaml only and remove the entire workspaces object from package.json.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@package.json` around lines 21 - 40, The package.json currently contains a
Bun-specific workspaces.catalogs object (the "workspaces" object with "catalogs"
and entries like "@cipherstash/auth", "tsup", etc.) which pnpm ignores; remove
the entire "workspaces" object from package.json and ensure any required catalog
definitions are present only in pnpm-workspace.yaml so there’s a single source
of truth for pnpm-managed catalogs; verify no other code references
package.json#workspaces before committing.

"scripts": {
"build": "turbo build --filter './packages/*'",
"build:js": "turbo build --filter './packages/protect' --filter './packages/nextjs'",
Expand All @@ -46,29 +62,6 @@
"node": ">=22"
},
"pnpm": {
"overrides": {
"@cipherstash/protect-ffi": "0.21.0",
"@babel/runtime": "7.26.10",
"brace-expansion@^5": ">=5.0.5",
"body-parser": "2.2.1",
"vite": "catalog:security",
"pg": "^8.16.3",
"postgres": "^3.4.7",
"js-yaml": "3.14.2",
"test-exclude": "^7.0.1",
"glob": ">=11.1.0",
"qs": ">=6.14.1",
"lodash": ">=4.17.23",
"minimatch": ">=10.2.3",
"@isaacs/brace-expansion": ">=5.0.1",
"fast-xml-parser": ">=5.3.4",
"next": ">=15.5.10",
"ajv": ">=8.18.0",
"esbuild@<=0.24.2": ">=0.25.0",
"picomatch@^4": ">=4.0.4",
"picomatch@^2": ">=2.3.2",
"rollup@>=4.0.0 <4.59.0": ">=4.59.0"
},
"peerDependencyRules": {
"ignoreMissing": [
"@types/pg",
Expand All @@ -80,5 +73,27 @@
}
},
"dedupe-peer-dependents": true
},
"overrides": {
"@babel/runtime": "7.26.10",
"brace-expansion@^5": ">=5.0.5",
"body-parser": "2.2.1",
"vite": "catalog:security",
"pg": "^8.16.3",
"postgres": "^3.4.7",
"js-yaml": "3.14.2",
"test-exclude": "^7.0.1",
"glob": ">=11.1.0",
"qs": ">=6.14.1",
"lodash": ">=4.17.23",
"minimatch": ">=10.2.3",
"@isaacs/brace-expansion": ">=5.0.1",
"fast-xml-parser": ">=5.3.4",
"next": ">=15.5.10",
"ajv": ">=8.18.0",
"esbuild@<=0.24.2": ">=0.25.0",
"picomatch@^4": ">=4.0.4",
"picomatch@^2": ">=2.3.2",
"rollup@>=4.0.0 <4.59.0": ">=4.59.0"
}
Comment on lines +77 to 98
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🌐 Web query:

Does pnpm support root-level overrides in package.json?

💡 Result:

Yes. pnpm supports root-level dependency overrides via the pnpm.overrides field in the root package.json (it applies to the whole dependency graph), and pnpm notes that this field can only be set at the project root. (dimava.github.io)

Example:

{
  "pnpm": {
    "overrides": {
      "foo": "^1.0.0",
      "bar@^2.1.0": "3.0.0",
      "qar@1>zoo": "2"
    }
  }
}
``` ([dimava.github.io](https://dimava.github.io/pnpm/package_json/))

Citations:

- 1: https://dimava.github.io/pnpm/package_json/
- 2: https://dimava.github.io/pnpm/package_json/

---

🏁 Script executed:

```shell
find . -name "package.json" -type f | head -5

Repository: cipherstash/stack

Length of output: 196


🏁 Script executed:

cat -n ./package.json | head -100

Repository: cipherstash/stack

Length of output: 3262


Move overrides inside the pnpm object.

The overrides field is at the root level of package.json (lines 77–98) but should be nested under pnpm.overrides. pnpm will not apply root-level overrides, which means the security-related overrides (e.g., vite, next, esbuild, rollup) are not being enforced. Move the entire overrides object into the existing pnpm configuration:

Suggested fix
  "pnpm": {
    "peerDependencyRules": {
      "ignoreMissing": [
        "@types/pg",
        "pg",
        "postgres"
      ],
      "allowedVersions": {
        "drizzle-orm": "*"
      }
    },
    "dedupe-peer-dependents": true,
    "overrides": {
      "@babel/runtime": "7.26.10",
      "brace-expansion@^5": ">=5.0.5",
      ...
    }
  }

The Node.js version requirement (>= 22) is correctly specified in the engines field.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@package.json` around lines 77 - 98, The root-level "overrides" object in
package.json must be moved into the existing "pnpm" configuration so pnpm will
apply them; remove the top-level "overrides" entry and add that same "overrides"
object as a child of the "pnpm" object (next to "peerDependencyRules" and
"dedupe-peer-dependents") so symbols like "@babel/runtime", "vite", "next",
"esbuild@<=0.24.2", and "rollup@>=4.0.0 <4.59.0" are enforced by pnpm.

}
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
# @cipherstash/stack-forge
# @cipherstash/cli

> 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`).

## 0.4.0

Expand Down
Loading
Loading