Documentazione
Banca dati
Persistenza SQLite con proprietà della connessione con ambito, errori di query digitate, backup di migrazione e archiviazione di documenti locali.
Tradotto automaticamente dall'inglese. Vedi l'originale in inglese
Modello di persistenza
@cadcamfun/db apre SQLite tramite bun:sqlite ed espone un database Drizzle all'interno dell'Effetto Database servizio. Il sidecar memorizza il database dell'applicazione in <dataDir>/cadcamfun.db. La connessione consente la registrazione write-ahead e l'imposizione di chiavi esterne prima di applicare le migrazioni.
Il database contiene la copia di lavoro dei progetti e le relative risorse. Anche gli oggetti di controllo della versione del progetto, le richieste pull e le esecuzioni del flusso di lavoro vengono archiviati localmente. Le credenziali del provider AI utilizzano un file JSON separato leggibile dal proprietario; il token di connessione dell'applicazione viene fornito all'avvio anziché essere persistente come preferenza.
Schema e punti di ingresso
packages/db/src/schema.ts definisce tabelle, indici, relazioni, valori predefiniti e tipi di selezione/inserimento dedotti. src/index.ts esportazioni Database, DatabaseError, la pioggerellina Db tipo e lo spazio dei nomi dello schema. src/upgrade.ts possiede la pianificazione della migrazione, i backup e l'applicazione transazionale; src/migrate.ts è il punto di ingresso della riga di comando. L'SQL generato e il diario di migrazione risiedono packages/db/drizzle.
Ogni connessione aperta da packages/db/src/index.ts passa a SQLite journal_mode = WAL e si accende foreign_keys. Viene eseguita la chiusura del database wal_checkpoint(TRUNCATE) quindi il file principale è completo quando il processo termina. Se il checkpoint è occupato o non funziona, viene registrato un avviso e l'arresto continua.
Valori memorizzati
Gli identificatori di dominio per impostazione predefinita sono UUID laddove configurato dalla tabella. Drizzle associa le colonne timestamp alle date JavaScript e le colonne JSON ai tipi di record dichiarati. I repository HTTP convertono le date nella loro rappresentazione wire e convalidano JSON al confine del dominio. Un'annotazione di tipo TypeScript su una colonna JSON archiviata non dimostra di per sé che i dati importati o precedenti corrispondano allo schema del dominio corrente.
Progetta i propri disegni, componenti e percorsi utensile. I percorsi utensile possono fare riferimento a un disegno, materiale, taglierina e configurazione della macchina; loro data sul campo può portare con sé un documento di fabbricazione portatile. Tabelle separate delle versioni di disegni, componenti e percorsi utensile conservano le versioni delle risorse oltre alla cronologia Git dell'intero progetto.
Interrogare tramite il servizio
Database.layer accetta un percorso file o :memory:, una cartella delle migrazioni facoltativa e una directory di backup facoltativa. Acquisisce l'handle SQLite per il suo ambito Effect. I chiamanti utilizzano query(operation, run) eseguire il lavoro Drizzle sincrono o asincrono; diventano eccezioni straniere DatabaseError con il nome dell'operazione e la causa originale.
import { Database, schema } from "@cadcamfun/db"
import { Effect } from "effect"
const loadProjects = Effect.gen(function* () {
const database = yield* Database
return yield* database.query("projects.list", (db) =>
db.select().from(schema.projectTable).all())
}) I repository possiedono regole di accesso e interpretazione delle righe; l'helper query condiviso fornisce l'attribuzione dell'errore. Non autorizza automaticamente una query né crea una transazione attorno a una sequenza arbitraria di chiamate di servizio.
Comportamento di aggiornamento
L'apertura di un livello con una cartella delle migrazioni legge i file di migrazione e confronta i loro nomi con __drizzle_migrations. Un journal del formato precedente viene riempito facendo corrispondere timestamp e hash di migrazione. Un journal ineguagliabile fallisce l'aggiornamento invece di indovinare un'identità di migrazione.
Quando un database su disco esistente necessita di un aggiornamento, il servizio crea un backup SQLite pre-migrazione utilizzando VACUUM INTO. Scrive su un file parziale, rinomina lo snapshot completato e conserva gli ultimi cinque backup pre-migrazione corrispondenti. La directory di backup predefinita è backups accanto al database. I database nuovi e in memoria non necessitano di questo backup pre-aggiornamento.
Le modifiche al journal e le istruzioni SQL in sospeso vengono eseguite in un'unica transazione immediata. Una migrazione non riuscita esegue il rollback della transazione e non riesce a costruire il livello. L'applicazione non deve annunciare lo stato di disponibilità quando l'inizializzazione del database non riesce.
Comandi di migrazione
bun run db:generate
bun run db:migrate Genera migrazioni dopo la modifica schema.tse mantieni insieme l'SQL generato e il journal. La CLI di migrazione utilizza DATABASE_URL quando fornito, rimuove un'iniziale file: prefisso e altrimenti si apre ./data/cadcamfun.db. Il wrapper root esegue questo script con packages/db come directory di lavoro, quindi la sua destinazione predefinita è packages/db/data/cadcamfun.db. Web Studio utilizza data/cadcamfun.db nella radice del repository. Il sidecar confezionato utilizza CADCAMFUN_DATA_DIR. Selezionare un percorso del database esplicito durante l'esecuzione della manutenzione in modo che questi punti di ingresso non operino involontariamente su file diversi.
Senza una cartella delle migrazioni, Database.layer apre il database ma non crea tabelle dell'applicazione. I test che richiedono lo schema completo forniscono alla directory di migrazione un database in memoria.
Domini di dati
- Identità e collaborazione: utenti, account, sessioni, autenticatori, token di verifica, abbonamenti, organizzazioni, appartenenze, inviti, conversazioni, messaggi, notifiche e abbonamenti push.
- Progettazione e produzione: progetti, disegni, componenti, relative versioni e commenti, materiali, strumenti, configurazioni di macchine, percorsi utensile e registrazioni dei costi di produzione.
- Servizi applicativi: file, elementi della libreria, registro/configurazione dei plug-in, chiavi API, record di analisi e attività, dati del marketplace, configurazione MCP e voci di valori-chiave locali.
- Storia del progetto: Repository Git, oggetti e riferimenti, richieste pull e revisioni ed esecuzioni del flusso di lavoro.
La presenza di una tabella stabilisce un modello di persistenza. Le integrazioni di rete e le funzionalità delle applicazioni dipendono dai gestori e dai servizi effettivi, piuttosto che dalla sola esistenza della tabella.
Archiviazione locale di valori-chiave
LocalKeyValue persiste lo stato dell'applicazione compatibile con JSON. Il sidecar KvStore supporta l'inserimento, l'upsert, l'eliminazione e l'elenco filtrato con prefisso. Le chiavi devono contenere testo diverso da spazi bianchi e contenere al massimo 512 caratteri. I valori consentono valori nulli, stringhe, booleani, numeri finiti, matrici e oggetti semplici; cicli, funzioni, numeri non finiti e oggetti non semplici vengono rifiutati.
Le voci includono un timestamp di aggiornamento. Gli upser sostituiscono il valore della stessa chiave. L'elenco dei prefissi viene ordinato per chiave e filtri in memoria. Utilizza questo negozio per le preferenze e il contesto dell'assistente; le credenziali appartengono al loro servizio credenziali dedicato.
Arresto e ripristino
Richieste il finalizzatore dell'ambito del database PRAGMA wal_checkpoint(TRUNCATE) prima di chiudere la maniglia. Un checkpoint occupato o un errore di chiusura vengono registrati come avviso in modo che i finalizzatori rimanenti possano essere eseguiti. Il proprietario della connessione, anziché i singoli gestori HTTP, chiude l'handle.
Per un backup manuale coerente, arrestare l'applicazione in modo pulito prima di copiare la directory dei dati oppure utilizzare le funzionalità di backup di SQLite mentre è in esecuzione. Un database live può avere uno stato nel suo file WAL. Per ispezionare un backup pre-migrazione, lavorare su una copia e conservare il database e i backup originali fino alla conferma del ripristino.
Responsabilità dell'archivio
Mantieni la creazione della connessione SQLite e la logica di migrazione in questo pacchetto. I repository Sidecar utilizzano il servizio di database fornito e associano gli errori di archiviazione agli errori HTTP della loro risorsa. Mantieni il calcolo della geometria nel core e i segreti nei servizi credenziali o token. I backup del database contengono dati di progetto e account e richiedono gli stessi controlli di accesso della directory dei dati attiva.
Ispezionare un database senza modificarlo
Dopo un arresto pulito, utilizzare la CLI SQLite rispetto a un percorso di database esplicito. La modalità di sola lettura evita la creazione accidentale di un nuovo file vuoto quando il percorso è sbagliato. Questi controlli esaminano la struttura e la coerenza senza esporre righe di account o credenziali.
sqlite3 -readonly /absolute/path/cadcamfun.db 'PRAGMA quick_check;'
sqlite3 -readonly /absolute/path/cadcamfun.db 'PRAGMA foreign_key_check;'
sqlite3 -readonly /absolute/path/cadcamfun.db "SELECT name FROM sqlite_master WHERE type = 'table' ORDER BY name;"
sqlite3 -readonly /absolute/path/cadcamfun.db 'SELECT name, applied_at FROM __drizzle_migrations ORDER BY id;' quick_check normalmente riporta ok; foreign_key_check restituisce le righe solo per le violazioni. Un diario senza name/applied_at columns è un formato precedente che il codice di aggiornamento dell'applicazione deve riconciliare prima di utilizzare l'ultima query. Controlla i percorsi e l'identità della migrazione prima di tentare le riparazioni.
L'applicazione imposta l'applicazione della chiave esterna sulla propria connessione. Una connessione di manutenzione aperta separatamente deve abilitarla esplicitamente prima di effettuare scritture autorizzate; il PRAGMA è specifico della connessione.
Fonte: packages/db/src/index.ts, upgrade.ts, e database.test.ts.
Utilizzare un database usa e getta nel codice
Un ambito completo del servizio in memoria è utile per esempi di repository e diagnostica isolata. L'ambito possiede la connessione e applica le migrazioni archiviate prima che l'effetto legga il servizio.
import { Database, schema } from "@cadcamfun/db"
import { Effect } from "effect"
const migrationsFolder = new URL("../../packages/db/drizzle", import.meta.url).pathname
const program = Effect.gen(function* () {
const database = yield* Database
return yield* database.query("diagnostic.projects", (db) =>
db.select({ id: schema.projectTable.id }).from(schema.projectTable).all())
}).pipe(Effect.provide(Database.layer({ path: ":memory:", migrationsFolder })))
console.log(await Effect.runPromise(program)) // [] Salva questo script con nome apps/sidecar/diagnostic.ts e corri bun apps/sidecar/diagnostic.ts dalla radice del repository. Questo pacchetto fornisce le dipendenze del database e degli effetti e l'URL di migrazione viene risolto da quel file. Una directory di migrazione mancante o errata non equivale a uno schema dell'applicazione vuoto. La pioggerellina query la richiamata può eseguire una query sincrona o restituire una promessa; l'assistente avvolge gli errori sotto l'etichetta dell'operazione fornita.
Fonte: packages/db/src/database.test.ts e index.ts.
Diagnosticare gli errori di archiviazione
- Nessuna tabella del genere: confermare l'esatto file di database e la cartella delle migrazioni. L'apertura senza migrazioni è consentita dal servizio, ma l'esecuzione di query sulle tabelle dell'applicazione non riesce.
- Vincolo di chiave esterna non riuscito: ispezionare il proprietario/progetto/risorsa a cui si fa riferimento. Le restrizioni sull'eliminazione sono specifiche dello schema: l'eliminazione di un utente con un progetto di proprietà è limitata, mentre le righe della sessione si sovrappongono quando l'utente viene eliminato.
- Backup non riuscito durante l'aggiornamento: controllare lo spazio libero e l'accesso in scrittura nel database e nelle directory di backup. L'errore del backup interrompe la migrazione; non eliminare il database originale per aggirarlo.
- Il journal delle migrazioni legacy non corrisponde alle migrazioni locali: preservare il database e ripristinare il set di migrazione corrispondente. La ridenominazione o la rimozione delle righe del journal nasconderebbe la cronologia degli aggiornamenti incompatibili.
- Checkpoint occupato allo spegnimento: ispezionare le altre connessioni aperte prima di copiare solo il file del database. Conserva i fratelli WAL/SHM fino al completamento di un checkpoint pulito o di un backup SQLite coerente.
I backup di migrazione sono punti di ripristino pre-aggiornamento con una policy di conservazione di cinque file, non un backup continuo delle modifiche successive. Eseguire il backup dell'intera directory dei dati separatamente in base ai requisiti di ripristino dell'installazione.
Fonte: packages/db/src/index.ts, upgrade.ts, schema.ts, e database.test.ts.
Storia della migrazione
Le migrazioni archiviate vengono applicate nell'ordine delle cartelle. Ogni cartella contiene un migration.sql e il kit pioviggine snapshot.json utilizzato per calcolare la differenza successiva. La riga del diario memorizza il nome della cartella, il suo hash, il timestamp della cartella in millisecondi (created_at) e il tempo ISO a cui è stato applicato (applied_at).
| Cartella | Misurare | Cosa crea |
|---|---|---|
20261004223029_careful_monster_badoon | 771 righe | Lo schema di base convertito dal modello Prisma legacy: identità, organizzazioni, progetti, disegni, componenti, percorsi utensile, librerie, costi, marketplace, plug-in e configurazione MCP. I nomi delle tabelle e delle colonne rimangono invariati, quindi più vecchi dev.db i dati rimangono leggibili. |
20261008030518_redundant_dagger | 73 righe | Storia del progetto: GitRepo, GitObject, GitRef, PullRequest, PullRequestReview, WorkflowRun. |
20261009012351_moaning_loki | 28 righe | Record del kernel della geometria: MeshAsset e CadOperationLog. |
20261009171602_brown_mad_thinker | 5 righe | LocalKeyValue (key chiave primaria del testo, value testo non nullo, updatedAt intero non nullo). |
I nomi delle cartelle provengono da drizzle-kit: un timestamp UTC seguito da un nome casuale. Non rinominare o modificare mai una cartella una volta che è stata applicata ovunque; il pianificatore di upgrade identifica le migrazioni applicate per nome e, per le riviste più vecchie, per timestamp e hash.
Aggiornare il flusso decisionale
| Situazione | Backup | Risultato |
|---|---|---|
| Nuovo file, cartella delle migrazioni fornita | No (il file non esisteva prima dell'apertura) | Tutte le migrazioni sono state applicate in un'unica transazione |
| File esistente, journal aggiornato | NO | Aperto così com'è; needed è falso |
| File esistente, migrazioni in sospeso | Sì, prima di qualsiasi cambiamento | Migrazioni in sospeso applicate; righe del diario inserite con applied_at |
File esistente, journal senza name colonna | SÌ | Colonne name e applied_at aggiunto, riempimento delle righe, quindi applicazione delle migrazioni in sospeso |
| La riga precedente non corrisponde ad alcuna migrazione locale | Non viene effettuato alcun backup: la pianificazione fallisce per prima | DatabaseError con operazione migrate: "Il diario delle migrazioni legacy non corrisponde alle migrazioni locali" |
:memory: | Mai | Migrazioni applicate se viene fornita una cartella |
| Nessuna cartella delle migrazioni | Mai | Aperto senza creare tabelle |
La corrispondenza del backfill funziona in questo modo: una riga legacy created_at viene troncato a secondi interi e confrontato con il timestamp della cartella di ciascuna migrazione. Se corrisponde esattamente a una migrazione, viene utilizzata. Altrimenti l'hash rompe il legame e come ultima risorsa viene utilizzata qualsiasi migrazione con lo stesso hash.
Anatomia del file di backup
backups/cadcamfun-20261015-034512.123-6f1c2d4e-8a90-4b7c-9e21-3f5a6b7c8d9e-pre-migrate.db
└ prefix ┘ └ UTC timestamp ┘ └ random UUID ┘ - Il prefisso è il nome del file del database senza
.db, seguito da un trattino. - Il timestamp è UTC con precisione al millisecondo. Se il backup esistente più recente ha lo stesso timestamp o uno successivo, quello nuovo viene impostato 1 ms dopo di esso, quindi l'ordine lessicale è sempre uguale all'ordine di creazione.
- Viene scritta la snapshot
<name>.partialconVACUUM INTO, poi rinominato. Un incidente lascia al massimo a.partialfile, che viene rimosso in afinallybloccare. - Dopo un backup riuscito, vengono conservati solo i cinque file più recenti che corrispondono al modello esatto. I file copiati nella directory con altri nomi non verranno mai eliminati.
VACUUM INTOproduce un database compatto e autonomo senza fratelli WAL, quindi è possibile aprire direttamente un backup.
DATA="/absolute/path/to/data"
cp "$DATA/cadcamfun.db" "$DATA/cadcamfun.db.broken" # keep the failed state
rm -f "$DATA/cadcamfun.db-wal" "$DATA/cadcamfun.db-shm"
cp "$DATA/backups/cadcamfun-<timestamp>-<uuid>-pre-migrate.db" "$DATA/cadcamfun.db"
sqlite3 -readonly "$DATA/cadcamfun.db" 'PRAGMA quick_check;' # expect: ok Il ripristino ripristina lo schema e i dati
Un backup ripristinato è lo stato subito prima dell'aggiornamento. Le modifiche apportate dopo l'aggiornamento non sono presenti e al lancio successivo verrà tentata nuovamente la stessa migrazione. Risolvere la causa dell'errore prima di riavviare.
Forme del tavolo centrale
Queste sono le tabelle che l'editor desktop, la cronologia e il kernel leggono più spesso. I nomi delle colonne sono i nomi SQL; Drizzle li espone sotto le stesse chiavi.
| Tavolo | Colonne |
|---|---|
Project | id, name, description, createdAt, updatedAt, ownerId, organizationId, isPublic |
Drawing | id, name, description, data, thumbnail, createdAt, updatedAt, projectId |
Component | id, name, description, data, thumbnail, type, isPublic, createdAt, updatedAt, projectId |
Toolpath | id, name, description, data, type, operationType, gcode, thumbnail, isPublic, projectId, createdAt, updatedAt, createdBy, drawingId, materialId, toolId, machineConfigId |
GitRepo | projectId, head, remote, createdAt |
GitObject | projectId, oid, type, body |
GitRef | projectId, name, oid |
PullRequest | id, projectId, number, title, body, head, base, state, authorId, authorKind, mergeCommit, remoteNumber, createdAt, updatedAt |
WorkflowRun | id, projectId, workflow, event, ref, sha, status, events, startedAt, finishedAt |
MeshAsset | id, projectId, kind, inputs, stats, mesh, createdAt |
CadOperationLog | id, projectId, operation, meshAssetId, outcome, durationMs, inputs, error, traceId, spanId, createdAt |
LocalKeyValue | key, value, updatedAt |
L'eliminazione di un progetto ne rimuove i disegni, i componenti, i percorsi utensile, gli oggetti e i riferimenti della cronologia, le richieste pull, le esecuzioni del flusso di lavoro, le risorse mesh e i registri delle operazioni. Le righe della versione e dei commenti procedono a cascata dalla risorsa principale. UN CadOperationLog la riga sopravvive all'eliminazione del relativo asset mesh con meshAssetId impostato su nullo. I collegamenti del percorso utensile a materiali, utensili e configurazioni della macchina non hanno una cascata, quindi un materiale di riferimento non può essere eliminato mentre un percorso utensile punta ad esso.
Catalogo completo delle tavole
Lo schema definisce 60 tabelle. L'elenco seguente è stato prodotto dalle definizioni della tabella Drizzle: nome SQL, costante esportata, conteggio delle colonne, numero di indici dichiarati e chiavi esterne con la relativa regola di eliminazione. "nessuna azione" è l'impostazione predefinita di SQLite: l'eliminazione fallisce mentre esistono righe di riferimento (le chiavi esterne vengono applicate alla connessione dell'applicazione).
| tabella SQL | Esportare | Col | Idx | Chiavi esterne |
|---|---|---|---|---|
Account | accountTable | 13 | 1 | userId → Utente (cascata) |
ActivityLog | activityLogTable | 7 | 0 | userId → Utente (nessuna azione) |
AIAnalysisLog | aIAnalysisLogTable | 7 | 0 | userId → Utente (nessuna azione) |
ApiKey | apiKeyTable | 7 | 1 | userId → Utente (cascata) |
authenticators | authenticatorTable | 8 | 0 | userId → Utente (cascata) |
CadOperationLog | cadOperationLogTable | 11 | 1 | projectId → Progetto (a cascata)meshAssetId → MeshAsset (imposta null) |
Component | componentTable | 10 | 0 | projectId → Progetto (a cascata) |
ComponentComment | componentCommentTable | 6 | 0 | componentId → Componente (cascata)userId → Utente (nessuna azione) |
ComponentVersion | componentVersionTable | 6 | 0 | componentId → Componente (cascata)userId → Utente (nessuna azione) |
Conversation | conversationTable | 6 | 0 | organizationId → Organizzazione (a cascata) |
ConversationParticipant | conversationParticipantTable | 5 | 1 | userId → Utente (cascata)conversationId → Conversazione (a cascata) |
CostSettings | costSettingsTable | 11 | 0 | ownerId → Utente (nessuna azione)organizationId → Organizzazione (nessuna azione) |
Drawing | drawingTable | 8 | 0 | projectId → Progetto (a cascata) |
DrawingVersion | drawingVersionTable | 5 | 0 | drawingId → Disegno (a cascata) |
FileUpload | fileUploadTable | 12 | 0 | — |
GitObject | gitObjectTable | 4 | 1 | projectId → Progetto (a cascata) |
GitRef | gitRefTable | 3 | 1 | projectId → Progetto (a cascata) |
GitRepo | gitRepoTable | 4 | 0 | projectId → Progetto (a cascata) |
LibraryItem | libraryItemTable | 14 | 0 | ownerId → Utente (nessuna azione)organizationId → Organizzazione (nessuna azione) |
LocalKeyValue | localKeyValueTable | 3 | 0 | — |
MachineConfig | machineConfigTable | 9 | 0 | ownerId → Utente (nessuna azione) |
MarketplaceCategory | marketplaceCategoryTable | 10 | 1 | parentId → Categoria Marketplace (nessuna azione) |
MarketplaceCollection | marketplaceCollectionTable | 10 | 1 | sellerId → MarketplaceSeller (nessuna azione) |
MarketplaceCollectionItem | marketplaceCollectionItemTable | 5 | 1 | collectionId → MarketplaceCollection (a cascata)itemId → MarketplaceItem (cascata) |
MarketplaceItem | marketplaceItemTable | 30 | 5 | sellerId → MarketplaceSeller (nessuna azione) |
MarketplaceOrder | marketplaceOrderTable | 23 | 4 | buyerId → Utente (nessuna azione)sellerId → MarketplaceSeller (nessuna azione)itemId → MarketplaceItem (nessuna azione) |
MarketplacePayout | marketplacePayoutTable | 12 | 1 | sellerId → MarketplaceSeller (nessuna azione) |
MarketplaceReview | marketplaceReviewTable | 12 | 2 | itemId → MarketplaceItem (nessuna azione)buyerId → Utente (nessuna azione)orderId → MarketplaceOrder (nessuna azione) |
MarketplaceSeller | marketplaceSellerTable | 19 | 0 | userId → Utente (nessuna azione) |
MarketplaceWishlist | marketplaceWishlistTable | 4 | 1 | userId → Utente (cascata)itemId → MarketplaceItem (cascata) |
Material | materialTable | 9 | 0 | ownerId → Utente (nessuna azione)organizationId → Organizzazione (nessuna azione) |
MaterialCost | materialCostTable | 10 | 0 | materialId → Materiale (cascata)ownerId → Utente (nessuna azione)organizationId → Organizzazione (nessuna azione) |
MCPServerConfig | mCPServerConfigTable | 11 | 1 | — |
MeshAsset | meshAssetTable | 7 | 1 | projectId → Progetto (a cascata) |
Message | messageTable | 8 | 1 | senderId → Utente (cascata)conversationId → Conversazione (a cascata)fileId → FileUpload (imposta null) |
Notification | notificationTable | 12 | 0 | userId → Utente (cascata)organizationId → Organizzazione (a cascata)projectId → Progetto (a cascata)componentId → Componente (cascata)messageId → Messaggio (a cascata) |
OperationCost | operationCostTable | 10 | 0 | ownerId → Utente (nessuna azione)organizationId → Organizzazione (nessuna azione) |
Organization | organizationTable | 5 | 0 | — |
OrganizationInvitation | organizationInvitationTable | 7 | 0 | organizationId → Organizzazione (a cascata) |
passkey | passkeyTable | 11 | 0 | userId → Utente (cascata) |
PluginConfiguration | pluginConfigurationTable | 2 | 0 | pluginId → PluginRegistryEntry (a cascata) |
PluginRegistryEntry | pluginRegistryEntryTable | 9 | 0 | — |
ProductionCostEstimate | productionCostEstimateTable | 17 | 0 | toolpathId → Percorso utensile (cascata)operationCostId → OperationCost (nessuna azione)ownerId → Utente (nessuna azione)organizationId → Organizzazione (nessuna azione) |
Project | projectTable | 8 | 0 | ownerId → Utente (nessuna azione)organizationId → Organizzazione (nessuna azione) |
PullRequest | pullRequestTable | 14 | 1 | projectId → Progetto (a cascata)authorId → Utente (nessuna azione) |
PullRequestReview | pullRequestReviewTable | 8 | 1 | pullRequestId → PullRequest (a cascata)reviewerId → Utente (nessuna azione) |
PushSubscription | pushSubscriptionTable | 6 | 0 | userId → Utente (cascata) |
Session | sessionTable | 8 | 0 | userId → Utente (cascata) |
Subscription | subscriptionTable | 24 | 3 | referenceId → Utente (cascata) |
Token | tokenTable | 8 | 2 | userId → Utente (cascata) |
Tool | toolTable | 17 | 0 | ownerId → Utente (nessuna azione)organizationId → Organizzazione (nessuna azione) |
Toolpath | toolpathTable | 17 | 0 | projectId → Progetto (a cascata)createdBy → Utente (nessuna azione)drawingId → Disegno (nessuna azione)materialId → Materiale (nessuna azione)toolId → Strumento (nessuna azione)machineConfigId → MachineConfig (nessuna azione) |
ToolpathComment | toolpathCommentTable | 6 | 0 | toolpathId → Percorso utensile (cascata)userId → Utente (nessuna azione) |
ToolpathVersion | toolpathVersionTable | 7 | 0 | toolpathId → Percorso utensile (cascata)userId → Utente (nessuna azione) |
ToolWearCost | toolWearCostTable | 10 | 0 | toolId → Strumento (cascata)ownerId → Utente (nessuna azione)organizationId → Organizzazione (nessuna azione) |
twoFactor | twoFactorTable | 4 | 0 | userId → Utente (cascata) |
User | userTable | 17 | 0 | — |
UserOrganization | userOrganizationTable | 5 | 1 | userId → Utente (cascata)organizationId → Organizzazione (a cascata) |
verification_tokens | verificationTokenTable | 3 | 1 | — |
WorkflowRun | workflowRunTable | 10 | 1 | projectId → Progetto (a cascata) |
Scrivere in sicurezza
- Racchiudi le modifiche a più istruzioni in una transazione Drizzle all'interno di una
querychiamata:database.query("op", (db) => db.transaction((tx) => ...)). Separatoqueryle chiamate sono transazioni separate. - Assegna a ciascuna query un nome di operazione stabile e punteggiato (
projects.list,toolpaths.update). È l'unico contesto aDatabaseErrorporta con sé la causa e appare nei log. - Genera ID con
newId()(un UUID) o lasciare che lo faccia la colonna predefinita; non creare ID dai nomi. - Convalida le colonne JSON durante la lettura. Il tipo TypeScript dichiarato non viene controllato da SQLite.
- Utilizzo
LocalKeyValuetramite il servizio valore-chiave collaterale anziché scrivere direttamente le righe, quindi viene applicata la convalida della chiave e del valore.
import { Database, schema } from "@cadcamfun/db"
import { eq } from "drizzle-orm"
import { Effect } from "effect"
const renameProject = (id: string, name: string) => Effect.gen(function* () {
const database = yield* Database
return yield* database.query("projects.rename", (db) =>
db.transaction((tx) => {
const updated = tx.update(schema.projectTable)
.set({ name, updatedAt: new Date() })
.where(eq(schema.projectTable.id, id))
.returning().all()
if (updated.length === 0) throw new Error(`project ${id} not found`)
return updated[0]
}))
}) Per creare righe con ID espliciti, utilizzare schema.newId(); @cadcamfun/db riesporta il modulo dello schema come schema spazio dei nomi, non come nomi di livello superiore.