Vai al contenuto
CADCAMFUN
Documentazione

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.

Una query sul repository
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

Esegui dalla radice del repository
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.

Ispezione SQLite di sola lettura
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.

Apri, migra, interroga e rilascia
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).

CartellaMisurareCosa crea
20261004223029_careful_monster_badoon771 righeLo 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_dagger73 righeStoria del progetto: GitRepo, GitObject, GitRef, PullRequest, PullRequestReview, WorkflowRun.
20261009012351_moaning_loki28 righeRecord del kernel della geometria: MeshAsset e CadOperationLog.
20261009171602_brown_mad_thinker5 righeLocalKeyValue (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

aprire il fileWAL + chiavi_estranee planUpgradeleggere il diario + file necessario?legacy o in sospeso > 0 no: pioviggine()servizio pronto backupDatabasesolo se il file esisteva applyUpgradeun messaggio IMMEDIATO fallimentoErrore database, rollback sÌ
SituazioneBackupRisultato
Nuovo file, cartella delle migrazioni fornitaNo (il file non esisteva prima dell'apertura)Tutte le migrazioni sono state applicate in un'unica transazione
File esistente, journal aggiornatoNOAperto così com'è; needed è falso
File esistente, migrazioni in sospesoSì, prima di qualsiasi cambiamentoMigrazioni in sospeso applicate; righe del diario inserite con applied_at
File esistente, journal senza name colonnaSÌColonne name e applied_at aggiunto, riempimento delle righe, quindi applicazione delle migrazioni in sospeso
La riga precedente non corrisponde ad alcuna migrazione localeNon viene effettuato alcun backup: la pianificazione fallisce per primaDatabaseError con operazione migrate: "Il diario delle migrazioni legacy non corrisponde alle migrazioni locali"
:memory:MaiMigrazioni applicate se viene fornita una cartella
Nessuna cartella delle migrazioniMaiAperto 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

Nome del backup pre-migrazione
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>.partial con VACUUM INTO, poi rinominato. Un incidente lascia al massimo a .partial file, che viene rimosso in a finally bloccare.
  • 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 INTO produce un database compatto e autonomo senza fratelli WAL, quindi è possibile aprire direttamente un backup.
Ripristinare un backup pre-migrazione (applicazione arrestata)
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.

TavoloColonne
Projectid, name, description, createdAt, updatedAt, ownerId, organizationId, isPublic
Drawingid, name, description, data, thumbnail, createdAt, updatedAt, projectId
Componentid, name, description, data, thumbnail, type, isPublic, createdAt, updatedAt, projectId
Toolpathid, name, description, data, type, operationType, gcode, thumbnail, isPublic, projectId, createdAt, updatedAt, createdBy, drawingId, materialId, toolId, machineConfigId
GitRepoprojectId, head, remote, createdAt
GitObjectprojectId, oid, type, body
GitRefprojectId, name, oid
PullRequestid, projectId, number, title, body, head, base, state, authorId, authorKind, mergeCommit, remoteNumber, createdAt, updatedAt
WorkflowRunid, projectId, workflow, event, ref, sha, status, events, startedAt, finishedAt
MeshAssetid, projectId, kind, inputs, stats, mesh, createdAt
CadOperationLogid, projectId, operation, meshAssetId, outcome, durationMs, inputs, error, traceId, spanId, createdAt
LocalKeyValuekey, value, updatedAt
Progetto DisegnoComponentePercorso utensileTabelle Git*PullRequestMeshAssetWorkflowRun DrawingVersionComp. VersionePercorso utensile ver.CadOpLogCadOpLog.mesh→null linee continue: ON CANCELLA CASCATA

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 SQLEsportareColIdxChiavi esterne
AccountaccountTable131userId → Utente (cascata)
ActivityLogactivityLogTable70userId → Utente (nessuna azione)
AIAnalysisLogaIAnalysisLogTable70userId → Utente (nessuna azione)
ApiKeyapiKeyTable71userId → Utente (cascata)
authenticatorsauthenticatorTable80userId → Utente (cascata)
CadOperationLogcadOperationLogTable111projectId → Progetto (a cascata)
meshAssetId → MeshAsset (imposta null)
ComponentcomponentTable100projectId → Progetto (a cascata)
ComponentCommentcomponentCommentTable60componentId → Componente (cascata)
userId → Utente (nessuna azione)
ComponentVersioncomponentVersionTable60componentId → Componente (cascata)
userId → Utente (nessuna azione)
ConversationconversationTable60organizationId → Organizzazione (a cascata)
ConversationParticipantconversationParticipantTable51userId → Utente (cascata)
conversationId → Conversazione (a cascata)
CostSettingscostSettingsTable110ownerId → Utente (nessuna azione)
organizationId → Organizzazione (nessuna azione)
DrawingdrawingTable80projectId → Progetto (a cascata)
DrawingVersiondrawingVersionTable50drawingId → Disegno (a cascata)
FileUploadfileUploadTable120—
GitObjectgitObjectTable41projectId → Progetto (a cascata)
GitRefgitRefTable31projectId → Progetto (a cascata)
GitRepogitRepoTable40projectId → Progetto (a cascata)
LibraryItemlibraryItemTable140ownerId → Utente (nessuna azione)
organizationId → Organizzazione (nessuna azione)
LocalKeyValuelocalKeyValueTable30—
MachineConfigmachineConfigTable90ownerId → Utente (nessuna azione)
MarketplaceCategorymarketplaceCategoryTable101parentId → Categoria Marketplace (nessuna azione)
MarketplaceCollectionmarketplaceCollectionTable101sellerId → MarketplaceSeller (nessuna azione)
MarketplaceCollectionItemmarketplaceCollectionItemTable51collectionId → MarketplaceCollection (a cascata)
itemId → MarketplaceItem (cascata)
MarketplaceItemmarketplaceItemTable305sellerId → MarketplaceSeller (nessuna azione)
MarketplaceOrdermarketplaceOrderTable234buyerId → Utente (nessuna azione)
sellerId → MarketplaceSeller (nessuna azione)
itemId → MarketplaceItem (nessuna azione)
MarketplacePayoutmarketplacePayoutTable121sellerId → MarketplaceSeller (nessuna azione)
MarketplaceReviewmarketplaceReviewTable122itemId → MarketplaceItem (nessuna azione)
buyerId → Utente (nessuna azione)
orderId → MarketplaceOrder (nessuna azione)
MarketplaceSellermarketplaceSellerTable190userId → Utente (nessuna azione)
MarketplaceWishlistmarketplaceWishlistTable41userId → Utente (cascata)
itemId → MarketplaceItem (cascata)
MaterialmaterialTable90ownerId → Utente (nessuna azione)
organizationId → Organizzazione (nessuna azione)
MaterialCostmaterialCostTable100materialId → Materiale (cascata)
ownerId → Utente (nessuna azione)
organizationId → Organizzazione (nessuna azione)
MCPServerConfigmCPServerConfigTable111—
MeshAssetmeshAssetTable71projectId → Progetto (a cascata)
MessagemessageTable81senderId → Utente (cascata)
conversationId → Conversazione (a cascata)
fileId → FileUpload (imposta null)
NotificationnotificationTable120userId → Utente (cascata)
organizationId → Organizzazione (a cascata)
projectId → Progetto (a cascata)
componentId → Componente (cascata)
messageId → Messaggio (a cascata)
OperationCostoperationCostTable100ownerId → Utente (nessuna azione)
organizationId → Organizzazione (nessuna azione)
OrganizationorganizationTable50—
OrganizationInvitationorganizationInvitationTable70organizationId → Organizzazione (a cascata)
passkeypasskeyTable110userId → Utente (cascata)
PluginConfigurationpluginConfigurationTable20pluginId → PluginRegistryEntry (a cascata)
PluginRegistryEntrypluginRegistryEntryTable90—
ProductionCostEstimateproductionCostEstimateTable170toolpathId → Percorso utensile (cascata)
operationCostId → OperationCost (nessuna azione)
ownerId → Utente (nessuna azione)
organizationId → Organizzazione (nessuna azione)
ProjectprojectTable80ownerId → Utente (nessuna azione)
organizationId → Organizzazione (nessuna azione)
PullRequestpullRequestTable141projectId → Progetto (a cascata)
authorId → Utente (nessuna azione)
PullRequestReviewpullRequestReviewTable81pullRequestId → PullRequest (a cascata)
reviewerId → Utente (nessuna azione)
PushSubscriptionpushSubscriptionTable60userId → Utente (cascata)
SessionsessionTable80userId → Utente (cascata)
SubscriptionsubscriptionTable243referenceId → Utente (cascata)
TokentokenTable82userId → Utente (cascata)
TooltoolTable170ownerId → Utente (nessuna azione)
organizationId → Organizzazione (nessuna azione)
ToolpathtoolpathTable170projectId → Progetto (a cascata)
createdBy → Utente (nessuna azione)
drawingId → Disegno (nessuna azione)
materialId → Materiale (nessuna azione)
toolId → Strumento (nessuna azione)
machineConfigId → MachineConfig (nessuna azione)
ToolpathCommenttoolpathCommentTable60toolpathId → Percorso utensile (cascata)
userId → Utente (nessuna azione)
ToolpathVersiontoolpathVersionTable70toolpathId → Percorso utensile (cascata)
userId → Utente (nessuna azione)
ToolWearCosttoolWearCostTable100toolId → Strumento (cascata)
ownerId → Utente (nessuna azione)
organizationId → Organizzazione (nessuna azione)
twoFactortwoFactorTable40userId → Utente (cascata)
UseruserTable170—
UserOrganizationuserOrganizationTable51userId → Utente (cascata)
organizationId → Organizzazione (a cascata)
verification_tokensverificationTokenTable31—
WorkflowRunworkflowRunTable101projectId → Progetto (a cascata)

Scrivere in sicurezza

  • Racchiudi le modifiche a più istruzioni in una transazione Drizzle all'interno di una query chiamata: database.query("op", (db) => db.transaction((tx) => ...)). Separato query le chiamate sono transazioni separate.
  • Assegna a ciascuna query un nome di operazione stabile e punteggiato (projects.list, toolpaths.update). È l'unico contesto a DatabaseError porta 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 LocalKeyValue tramite il servizio valore-chiave collaterale anziché scrivere direttamente le righe, quindi viene applicata la convalida della chiave e del valore.
Una scrittura transazionale
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.

Nel 3D

Schema del database in 3D Trascina per orbitare · scorri per ingrandire · trascina con il tasto destro per eseguire la panoramica
Tutte le 60 tabelle delle migrazioni di Drizzle in packages/db/drizzle; ogni riga è una chiave esterna REFERENCES (91 tra tabelle diverse). Gli hub al centro sono i tavoli più referenziati.