@lucashw68/nsdb 1.0.0-rc.2

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.
Files changed (49) hide show
  1. package/CHANGELOG.md +34 -0
  2. package/GET_STARTED.md +709 -0
  3. package/LICENSE +21 -0
  4. package/README.md +159 -0
  5. package/cli/index.js +83 -0
  6. package/helpers/args.js +22 -0
  7. package/helpers/config.js +142 -0
  8. package/helpers/generated.js +48 -0
  9. package/helpers/io.js +39 -0
  10. package/helpers/metadata.js +19 -0
  11. package/helpers/names.js +16 -0
  12. package/helpers/relations.js +101 -0
  13. package/helpers/shell.js +15 -0
  14. package/helpers/tables.js +79 -0
  15. package/helpers/ts.js +37 -0
  16. package/module.ts +151 -0
  17. package/nsdb.config.example.mjs +39 -0
  18. package/nsdb.config.example.ts +42 -0
  19. package/package.json +114 -0
  20. package/runtime/components/Form/NsdbRelationSelect.vue +258 -0
  21. package/runtime/components/NsdbForm.vue +865 -0
  22. package/runtime/components/NsdbList.vue +961 -0
  23. package/runtime/composables/useNsdbProfile.ts +119 -0
  24. package/runtime/composables/useNsdbSchemas.ts +176 -0
  25. package/runtime/composables/useSupabaseApi.ts +177 -0
  26. package/runtime/composables/useSupabaseApiStorage.ts +337 -0
  27. package/runtime/composables/useSupabaseModels.ts +412 -0
  28. package/runtime/query.ts +126 -0
  29. package/runtime/stores/createDbStore.ts +439 -0
  30. package/runtime/stores/createSingletonDbStore.ts +67 -0
  31. package/runtime/utils/dataFreshness.ts +47 -0
  32. package/runtime/utils/storage.ts +41 -0
  33. package/scripts/clear.js +64 -0
  34. package/scripts/generate-composables.js +100 -0
  35. package/scripts/generate-enums.js +106 -0
  36. package/scripts/generate-metadata.js +165 -0
  37. package/scripts/generate-models.js +164 -0
  38. package/scripts/generate-schemas.js +443 -0
  39. package/scripts/generate-stores.js +90 -0
  40. package/scripts/generate-types.js +196 -0
  41. package/scripts/init.js +225 -0
  42. package/templates/model.template.ts +48 -0
  43. package/templates/schema.template.ts +13 -0
  44. package/templates/useNsdbModel.template.ts +9 -0
  45. package/types/config.ts +50 -0
  46. package/types/entities.ts +66 -0
  47. package/types/index.ts +14 -0
  48. package/types/list.ts +78 -0
  49. package/types/model.ts +57 -0
@@ -0,0 +1,196 @@
1
+ #!/usr/bin/env node
2
+ import path from 'path'
3
+ import { parseArgs } from '../helpers/args.js'
4
+ import { ensureDir, readText, writeText } from '../helpers/io.js'
5
+ import { run, isAvailable } from '../helpers/shell.js'
6
+ import { getBoolOption, getOption, loadNsdbConfig } from '../helpers/config.js'
7
+
8
+ /**
9
+ * Build the Supabase CLI command that dumps the database types to disk.
10
+ */
11
+ function quoteShellArgument(value) {
12
+ return `'${String(value).replaceAll("'", "'\\''")}'`
13
+ }
14
+
15
+ function quoteRemoteShellArgument(value) {
16
+ return `"${String(value)
17
+ .replaceAll('\\', '\\\\')
18
+ .replaceAll('"', '\\"')
19
+ .replaceAll('`', '\\`')}"`
20
+ }
21
+
22
+ export function buildCommand({
23
+ projectId,
24
+ dbUrl,
25
+ outputPath,
26
+ schemaName,
27
+ useLinkedProject,
28
+ supabaseCommand = 'npx supabase',
29
+ argumentQuoter = quoteShellArgument,
30
+ }) {
31
+ const parts = [`${supabaseCommand} gen types typescript`]
32
+ if (schemaName) parts.push(`--schema ${schemaName}`)
33
+ if (dbUrl) {
34
+ parts.push(`--db-url ${argumentQuoter(dbUrl)}`)
35
+ } else {
36
+ parts.push(useLinkedProject ? '--linked' : `--project-id ${projectId}`)
37
+ }
38
+ parts.push(`> ${argumentQuoter(outputPath)}`)
39
+ return parts.join(' ')
40
+ }
41
+
42
+ export function buildLocalTypesEnvironment({ dbUrl, environment = process.env }) {
43
+ const commandEnvironment = { ...environment }
44
+ if (dbUrl) delete commandEnvironment.SUPABASE_PROJECT_ID
45
+ return commandEnvironment
46
+ }
47
+
48
+ export function buildRemoteTypesCommands({
49
+ sshHost,
50
+ projectPath,
51
+ dbUrl,
52
+ remoteOutput = '/tmp/database.types.ts',
53
+ localOutputPath,
54
+ schemaName = 'public',
55
+ beforeCommand = '',
56
+ supabaseCommand = 'npx supabase',
57
+ }) {
58
+ const remoteGenerateCommand = [
59
+ projectPath ? `cd ${quoteShellArgument(projectPath)}` : '',
60
+ beforeCommand,
61
+ buildCommand({
62
+ dbUrl,
63
+ outputPath: remoteOutput,
64
+ schemaName,
65
+ useLinkedProject: false,
66
+ supabaseCommand,
67
+ argumentQuoter: quoteRemoteShellArgument,
68
+ }),
69
+ ]
70
+ .filter(Boolean)
71
+ .join(' && ')
72
+
73
+ return {
74
+ generateCommand: `ssh ${quoteShellArgument(sshHost)} ${quoteShellArgument(remoteGenerateCommand)}`,
75
+ copyCommand: `scp ${quoteShellArgument(`${sshHost}:${remoteOutput}`)} ${quoteShellArgument(localOutputPath)}`,
76
+ }
77
+ }
78
+
79
+ async function loadDotenvIfAvailable(dotenvFilePath) {
80
+ try {
81
+ const dotenvModule = await import('dotenv')
82
+ dotenvModule.config({ path: dotenvFilePath })
83
+ } catch (error) {
84
+ const errorCode = error?.code || error?.cause?.code
85
+ if (errorCode !== 'ERR_MODULE_NOT_FOUND') {
86
+ console.warn(`⚠️ Unable to load dotenv file at ${dotenvFilePath}:`, error?.message || error)
87
+ }
88
+ }
89
+ }
90
+
91
+ function normalizeGeneratedTypes(filePath) {
92
+ writeText(filePath, `${readText(filePath).trimEnd()}\n`)
93
+ }
94
+
95
+ async function main() {
96
+ const parsedArguments = parseArgs()
97
+ const currentWorkingDirectory = process.cwd()
98
+ const { config } = await loadNsdbConfig(currentWorkingDirectory, parsedArguments.get('config', ''))
99
+ const dotenvFilePath = path.resolve(currentWorkingDirectory, parsedArguments.get('dotenv', '.env'))
100
+ await loadDotenvIfAvailable(dotenvFilePath)
101
+
102
+ const outputFilePath = path.resolve(currentWorkingDirectory, getOption(parsedArguments, config, 'out', 'paths.types'))
103
+ const projectId = getOption(parsedArguments, config, 'project-id', 'supabase.projectId', process.env.SUPABASE_PROJECT_ID || '')
104
+ const dbUrl = getOption(parsedArguments, config, 'db-url', 'supabase.dbUrl', process.env.SUPABASE_DB_URL || '')
105
+ const schemaName = getOption(parsedArguments, config, 'schema', 'supabase.schema', 'public')
106
+ const useLinkedProject = getBoolOption(parsedArguments, config, 'linked', 'supabase.linked', false)
107
+ const remoteSshHost = getOption(parsedArguments, config, 'remote-ssh-host', 'supabase.remoteTypes.sshHost', process.env.SUPABASE_REMOTE_SSH_HOST || '')
108
+ const remoteProjectPath = getOption(parsedArguments, config, 'remote-project-path', 'supabase.remoteTypes.projectPath', process.env.SUPABASE_REMOTE_PROJECT_PATH || '')
109
+ const remoteDbUrl = getOption(parsedArguments, config, 'remote-db-url', 'supabase.remoteTypes.dbUrl', process.env.SUPABASE_REMOTE_DB_URL || '')
110
+ const remoteOutputPath = getOption(parsedArguments, config, 'remote-output', 'supabase.remoteTypes.remoteOutput', '/tmp/database.types.ts')
111
+ const remoteBeforeCommand = getOption(parsedArguments, config, 'remote-before-command', 'supabase.remoteTypes.beforeCommand', process.env.SUPABASE_REMOTE_BEFORE_COMMAND || '')
112
+ const remoteSupabaseCommand = getOption(parsedArguments, config, 'remote-supabase-command', 'supabase.remoteTypes.supabaseCommand', process.env.SUPABASE_REMOTE_SUPABASE_COMMAND || 'npx supabase')
113
+ const useRemoteTypes = Boolean(remoteSshHost)
114
+
115
+ if (useRemoteTypes && !remoteDbUrl) {
116
+ console.error('❌ Missing remote DB URL (pass --remote-db-url or set supabase.remoteTypes.dbUrl).')
117
+ process.exit(1)
118
+ }
119
+
120
+ if (!useRemoteTypes && !dbUrl && !useLinkedProject && !projectId) {
121
+ console.error('❌ Missing Supabase source (pass --remote-ssh-host, --db-url, --project-id or --linked).')
122
+ process.exit(1)
123
+ }
124
+
125
+ if (!useRemoteTypes && !isAvailable('npx supabase --version')) {
126
+ console.error('❌ Supabase CLI not available. Install it with: npm i -D supabase')
127
+ process.exit(1)
128
+ }
129
+
130
+ ensureDir(path.dirname(outputFilePath))
131
+ if (useRemoteTypes) {
132
+ const { generateCommand, copyCommand } = buildRemoteTypesCommands({
133
+ sshHost: remoteSshHost,
134
+ projectPath: remoteProjectPath,
135
+ dbUrl: remoteDbUrl,
136
+ remoteOutput: remoteOutputPath,
137
+ localOutputPath: outputFilePath,
138
+ schemaName,
139
+ beforeCommand: remoteBeforeCommand,
140
+ supabaseCommand: remoteSupabaseCommand,
141
+ })
142
+
143
+ console.log(`📦 Project: ${remoteSshHost} (remote ssh)`)
144
+ console.log(`📁 Output: ${path.relative(currentWorkingDirectory, outputFilePath)}`)
145
+ console.log(`📚 Schema: ${schemaName}`)
146
+ console.log(`🗄️ Remote output: ${remoteOutputPath}`)
147
+ console.log('🔄 Generating Supabase types remotely...')
148
+
149
+ try {
150
+ run(generateCommand, { inherit: true })
151
+ run(copyCommand, { inherit: true })
152
+ normalizeGeneratedTypes(outputFilePath)
153
+ console.log('✅ Types generated and copied successfully.')
154
+ } catch (error) {
155
+ console.error('❌ Failed to generate Supabase types remotely.')
156
+ process.exit(1)
157
+ }
158
+
159
+ return
160
+ }
161
+
162
+ const commandLine = buildCommand({
163
+ projectId,
164
+ dbUrl,
165
+ outputPath: outputFilePath,
166
+ schemaName,
167
+ useLinkedProject
168
+ })
169
+
170
+ console.log(`📦 Project: ${dbUrl ? '(db-url)' : useLinkedProject ? '(linked)' : projectId}`)
171
+ console.log(`📁 Output: ${path.relative(currentWorkingDirectory, outputFilePath)}`)
172
+ console.log(`📚 Schema: ${schemaName}`)
173
+ if (dbUrl) console.log('🗄️ DB URL mode enabled')
174
+ if (useLinkedProject) console.log('🔗 Linked project mode enabled')
175
+ console.log('🔄 Generating Supabase types...')
176
+
177
+ try {
178
+ run(commandLine, {
179
+ inherit: true,
180
+ env: buildLocalTypesEnvironment({ dbUrl }),
181
+ })
182
+ normalizeGeneratedTypes(outputFilePath)
183
+ console.log('✅ Types generated successfully.')
184
+ } catch (error) {
185
+ console.error('❌ Failed to generate Supabase types.')
186
+ process.exit(1)
187
+ }
188
+ }
189
+
190
+ if (import.meta.url === `file://${process.argv[1]}`) {
191
+ main().catch((error) => {
192
+ console.error('❌ Unexpected error while generating types.')
193
+ console.error(error)
194
+ process.exit(1)
195
+ })
196
+ }
@@ -0,0 +1,225 @@
1
+ #!/usr/bin/env node
2
+ import path from 'node:path'
3
+ import { parseArgs } from '../helpers/args.js'
4
+ import { ensureDir, exists, readText, writeText } from '../helpers/io.js'
5
+
6
+ const DEFAULT_PATHS = {
7
+ types: 'types/database.types.ts',
8
+ metadata: 'nsdb/database.metadata.json',
9
+ enums: 'nsdb/enums.ts',
10
+ schemas: 'nsdb/schemas',
11
+ models: 'nsdb/models',
12
+ composables: 'nsdb/composables',
13
+ stores: 'stores',
14
+ }
15
+
16
+ const NSDB_SCRIPTS = {
17
+ 'nsdb:init': 'nsdb init',
18
+ 'nsdb:types': 'nsdb generate:types',
19
+ 'nsdb:metadata': 'nsdb generate:metadata',
20
+ 'nsdb:enums': 'nsdb generate:enums',
21
+ 'nsdb:schemas': 'nsdb generate:schemas',
22
+ 'nsdb:models': 'nsdb generate:models',
23
+ 'nsdb:composables': 'nsdb generate:composables',
24
+ 'nsdb:stores': 'nsdb generate:stores',
25
+ 'nsdb:all': 'nsdb generate:all',
26
+ }
27
+
28
+ function quoteString(value) {
29
+ return `'${String(value).replaceAll('\\', '\\\\').replaceAll("'", "\\'")}'`
30
+ }
31
+
32
+ export function buildNsdbConfigTemplate({
33
+ schemaName = 'public',
34
+ projectIdExpression = 'process.env.SUPABASE_PROJECT_ID',
35
+ dbUrlExpression = '',
36
+ remoteTypes = null,
37
+ linked = false,
38
+ paths = DEFAULT_PATHS,
39
+ } = {}) {
40
+ const usesDefaultPaths = Object.entries(DEFAULT_PATHS).every(([key, value]) => paths[key] === value)
41
+ const projectIdLine = linked || dbUrlExpression || remoteTypes
42
+ ? ''
43
+ : `\n\t\tprojectId: ${projectIdExpression},`
44
+ const dbUrlLine = dbUrlExpression
45
+ ? `\n\t\tdbUrl: ${dbUrlExpression},`
46
+ : ''
47
+ const remoteTypesLine = remoteTypes
48
+ ? `\n\t\tremoteTypes: {
49
+ \t\t\tsshHost: ${remoteTypes.sshHost},
50
+ \t\t\tprojectPath: ${remoteTypes.projectPath},
51
+ \t\t\tdbUrl: ${remoteTypes.dbUrl},
52
+ \t\t\tremoteOutput: ${remoteTypes.remoteOutput},
53
+ \t\t\tbeforeCommand: ${remoteTypes.beforeCommand},
54
+ \t\t\tsupabaseCommand: ${remoteTypes.supabaseCommand},
55
+ \t\t},`
56
+ : ''
57
+ const pathsBlock = usesDefaultPaths
58
+ ? ''
59
+ : `
60
+ paths: {
61
+ ${Object.entries(paths).map(([key, value]) => `\t\t${key}: ${quoteString(value)},`).join('\n')}
62
+ },
63
+ imports: {
64
+ databaseTypes: '~~/types/database.types',
65
+ },`
66
+
67
+ return `import type { NsdbConfig } from '@lucashw68/nsdb/types/config'
68
+
69
+ export default {
70
+ \tsupabase: {
71
+ \t\tschema: ${quoteString(schemaName)},${projectIdLine}${dbUrlLine}${remoteTypesLine}
72
+ \t\tlinked: ${linked ? 'true' : 'false'},
73
+ \t},${pathsBlock}
74
+ } satisfies NsdbConfig
75
+ `
76
+ }
77
+
78
+ export function mergePackageScripts(packageJson, scriptsToAdd = NSDB_SCRIPTS) {
79
+ const nextPackageJson = {
80
+ ...packageJson,
81
+ scripts: {
82
+ ...(packageJson.scripts ?? {}),
83
+ },
84
+ }
85
+
86
+ for (const [scriptName, scriptCommand] of Object.entries(scriptsToAdd)) {
87
+ if (!nextPackageJson.scripts[scriptName]) {
88
+ nextPackageJson.scripts[scriptName] = scriptCommand
89
+ }
90
+ }
91
+
92
+ return nextPackageJson
93
+ }
94
+
95
+ function buildEnvExample() {
96
+ return `SUPABASE_URL=https://your-project.supabase.co
97
+ SUPABASE_KEY=your-anon-key
98
+ SUPABASE_PROJECT_ID=your-project-id
99
+ SUPABASE_DB_URL=postgresql://postgres:password@localhost:5432/postgres
100
+ SUPABASE_REMOTE_SSH_HOST=vps
101
+ SUPABASE_REMOTE_PROJECT_PATH=/opt/supabase-projects/example
102
+ SUPABASE_REMOTE_DB_URL=postgresql://postgres:$POSTGRES_PASSWORD@db:5432/postgres
103
+ SUPABASE_REMOTE_BEFORE_COMMAND=source ~/.nvm/nvm.sh
104
+ SUPABASE_REMOTE_SUPABASE_COMMAND=npx supabase
105
+ `
106
+ }
107
+
108
+ function writeFileIfAllowed(filePath, content, { force = false, label }) {
109
+ if (exists(filePath) && !force) {
110
+ console.log(`↷ ${label} already exists, skipped: ${path.relative(process.cwd(), filePath)}`)
111
+ return false
112
+ }
113
+
114
+ writeText(filePath, content)
115
+ console.log(`✓ ${label}: ${path.relative(process.cwd(), filePath)}`)
116
+ return true
117
+ }
118
+
119
+ function ensureConfiguredDirectories(currentWorkingDirectory, paths = DEFAULT_PATHS) {
120
+ const directories = [
121
+ path.dirname(paths.types),
122
+ path.dirname(paths.metadata),
123
+ path.dirname(paths.enums),
124
+ paths.schemas,
125
+ paths.models,
126
+ paths.composables,
127
+ paths.stores,
128
+ ]
129
+
130
+ for (const directoryPath of directories) {
131
+ const absoluteDirectoryPath = path.resolve(currentWorkingDirectory, directoryPath)
132
+ ensureDir(absoluteDirectoryPath)
133
+ console.log(`✓ Directory: ${path.relative(currentWorkingDirectory, absoluteDirectoryPath)}`)
134
+ }
135
+ }
136
+
137
+ function updatePackageJson(currentWorkingDirectory) {
138
+ const packageJsonPath = path.resolve(currentWorkingDirectory, 'package.json')
139
+
140
+ if (!exists(packageJsonPath)) {
141
+ console.log('↷ package.json not found, skipped scripts setup')
142
+ return false
143
+ }
144
+
145
+ const packageJson = JSON.parse(readText(packageJsonPath))
146
+ const nextPackageJson = mergePackageScripts(packageJson)
147
+ writeText(packageJsonPath, `${JSON.stringify(nextPackageJson, null, 2)}\n`)
148
+ console.log('✓ package.json scripts')
149
+ return true
150
+ }
151
+
152
+ function printNextSteps({ linked, usesDbUrl }) {
153
+ console.log('')
154
+ console.log('Next steps:')
155
+ console.log('1. Add @lucashw68/nsdb to modules in nuxt.config.ts.')
156
+ console.log('2. Configure @nuxtjs/supabase with SUPABASE_URL and SUPABASE_KEY.')
157
+ if (!linked && !usesDbUrl) console.log('3. Set SUPABASE_PROJECT_ID in .env.')
158
+ if (usesDbUrl) console.log('3. Set SUPABASE_DB_URL or SUPABASE_REMOTE_* variables in .env.')
159
+ console.log(`${linked && !usesDbUrl ? '3' : '4'}. Run: npm run nsdb:all`)
160
+ }
161
+
162
+ export async function initNsdb({
163
+ currentWorkingDirectory = process.cwd(),
164
+ parsedArguments = parseArgs(),
165
+ } = {}) {
166
+ const force = parsedArguments.getBool('force', false)
167
+ const linked = parsedArguments.getBool('linked', false)
168
+ const schemaName = parsedArguments.get('schema', 'public')
169
+ const projectId = parsedArguments.get('project-id', '')
170
+ const dbUrl = parsedArguments.get('db-url', '')
171
+ const remoteSshHost = parsedArguments.get('remote-ssh-host', '')
172
+ const remoteProjectPath = parsedArguments.get('remote-project-path', '')
173
+ const remoteDbUrl = parsedArguments.get('remote-db-url', '')
174
+ const remoteOutput = parsedArguments.get('remote-output', '/tmp/database.types.ts')
175
+ const remoteBeforeCommand = parsedArguments.get('remote-before-command', '')
176
+ const remoteSupabaseCommand = parsedArguments.get('remote-supabase-command', '')
177
+ const projectIdExpression = projectId ? quoteString(projectId) : 'process.env.SUPABASE_PROJECT_ID'
178
+ const dbUrlExpression = dbUrl ? quoteString(dbUrl) : parsedArguments.getBool('self-hosted', false) ? 'process.env.SUPABASE_DB_URL' : ''
179
+ const useRemoteTypes = Boolean(remoteSshHost) || parsedArguments.getBool('remote-types', false)
180
+ const remoteTypes = useRemoteTypes
181
+ ? {
182
+ sshHost: remoteSshHost ? quoteString(remoteSshHost) : 'process.env.SUPABASE_REMOTE_SSH_HOST',
183
+ projectPath: remoteProjectPath ? quoteString(remoteProjectPath) : 'process.env.SUPABASE_REMOTE_PROJECT_PATH',
184
+ dbUrl: remoteDbUrl ? quoteString(remoteDbUrl) : 'process.env.SUPABASE_REMOTE_DB_URL',
185
+ remoteOutput: quoteString(remoteOutput),
186
+ beforeCommand: remoteBeforeCommand ? quoteString(remoteBeforeCommand) : 'process.env.SUPABASE_REMOTE_BEFORE_COMMAND',
187
+ supabaseCommand: remoteSupabaseCommand ? quoteString(remoteSupabaseCommand) : 'process.env.SUPABASE_REMOTE_SUPABASE_COMMAND',
188
+ }
189
+ : null
190
+ const configFileName = parsedArguments.get('config', 'nsdb.config.ts')
191
+ const configFilePath = path.resolve(currentWorkingDirectory, configFileName)
192
+ const envExampleFilePath = path.resolve(currentWorkingDirectory, '.env.example')
193
+
194
+ console.log('🧬 Initializing NSDB...')
195
+
196
+ writeFileIfAllowed(
197
+ configFilePath,
198
+ buildNsdbConfigTemplate({
199
+ schemaName,
200
+ projectIdExpression,
201
+ dbUrlExpression,
202
+ remoteTypes,
203
+ linked,
204
+ paths: DEFAULT_PATHS,
205
+ }),
206
+ { force, label: 'Config' }
207
+ )
208
+
209
+ writeFileIfAllowed(envExampleFilePath, buildEnvExample(), {
210
+ force: false,
211
+ label: 'Env example',
212
+ })
213
+
214
+ ensureConfiguredDirectories(currentWorkingDirectory, DEFAULT_PATHS)
215
+ updatePackageJson(currentWorkingDirectory)
216
+ printNextSteps({ linked, usesDbUrl: Boolean(dbUrlExpression || remoteTypes) })
217
+ }
218
+
219
+ if (import.meta.url === `file://${process.argv[1]}`) {
220
+ initNsdb().catch((error) => {
221
+ console.error('❌ Failed to initialize NSDB.')
222
+ console.error(error)
223
+ process.exit(1)
224
+ })
225
+ }
@@ -0,0 +1,48 @@
1
+ // __IMPORTS__
2
+ import type { Tables, TablesInsert, TablesUpdate } from '~~/types/database.types'
3
+ import { __PASCAL__Relations, __PASCAL__Schema } from '~~/nsdb/schemas/__TABLE__'
4
+ import { useNsdbSchema } from '@lucashw68/nsdb/useNsdbSchema'
5
+ __STORE_IMPORT__
6
+
7
+ export type __ROW__ = Omit<Tables<'__TABLE__'>, __ROW_OMIT__>
8
+ export type __PASCAL__Insert = Omit<TablesInsert<'__TABLE__'>, __INSERT_OMIT__>
9
+ export type __PASCAL__Update = Omit<TablesUpdate<'__TABLE__'>, __UPDATE_OMIT__>
10
+ export type __PASCAL__RelationRows = __RELATION_ROWS__
11
+
12
+ export function __HOOK__(opts: { store?: boolean } = {}) {
13
+ const model = useSupabaseModel<__ROW__, __PASCAL__Insert, __PASCAL__Update, '__PRIMARY_KEY__'>(
14
+ '__TABLE__',
15
+ { store: !!opts.store, storeCreator: __STORE_CREATOR__, primaryKey: '__PRIMARY_KEY__' }
16
+ )
17
+
18
+ const {
19
+ fields,
20
+ editableKeys,
21
+ createDraftFromSchema,
22
+ bindModel,
23
+ } = useNsdbSchema(__PASCAL__Schema, __PASCAL__Relations);
24
+
25
+ const { fetch, refresh } = bindModel<__ROW__, __PASCAL__RelationRows>(model)
26
+
27
+ return {
28
+ primaryKey: '__PRIMARY_KEY__' as const,
29
+ items: model.items,
30
+ totalCount: model.totalCount,
31
+ loading: model.loading,
32
+ error: model.error,
33
+ stale: model.stale,
34
+ schema: __PASCAL__Schema,
35
+ fields,
36
+ editableKeys,
37
+ createDraft: () => createDraftFromSchema(),
38
+ fetch,
39
+ refresh,
40
+ invalidate: model.invalidate,
41
+ getById: model.getById,
42
+ create: model.create,
43
+ update: model.update,
44
+ remove: model.remove,
45
+ subscribe: model.subscribe,
46
+ unsubscribe: model.unsubscribe,
47
+ }
48
+ }
@@ -0,0 +1,13 @@
1
+ import type { Tables } from '~~/types/database.types'
2
+ import type { EntityField, EntityRelation } from '@lucashw68/nsdb/types/entities'
3
+ import * as Enums from '~~/nsdb/enums'
4
+
5
+ // Convenience row type for this table (optional)
6
+ export type __ROW__ = Tables<'__TABLE__'>
7
+
8
+ /** ----- Schema (entities) for __TABLE__ ----- */
9
+ export const __PASCAL__Schema = {
10
+ // __FIELDS__
11
+ } as const satisfies Partial<Record<keyof __ROW__, EntityField>>
12
+
13
+ export const __PASCAL__Relations: EntityRelation[] = __RELATIONS__
@@ -0,0 +1,9 @@
1
+ // __IMPORTS__
2
+
3
+ export function useNsdbModel(model: string, opts: { store?: boolean } = {}) {
4
+ switch (model) {
5
+ // __CASES__
6
+ default:
7
+ throw new Error(`Aucun model nsdb enregistré pour le modèle "${model}"`)
8
+ }
9
+ }
@@ -0,0 +1,50 @@
1
+ export interface NsdbConfig {
2
+ supabase?: {
3
+ schema?: string
4
+ projectId?: string
5
+ dbUrl?: string
6
+ linked?: boolean
7
+ remoteTypes?: {
8
+ sshHost?: string
9
+ projectPath?: string
10
+ dbUrl?: string
11
+ remoteOutput?: string
12
+ beforeCommand?: string
13
+ supabaseCommand?: string
14
+ }
15
+ }
16
+ paths?: {
17
+ types?: string
18
+ metadata?: string
19
+ enums?: string
20
+ schemas?: string
21
+ models?: string
22
+ composables?: string
23
+ stores?: string
24
+ }
25
+ imports?: {
26
+ databaseTypes?: string
27
+ }
28
+ tables?: {
29
+ /** Allowlist of tables exposed through generated NSDB artifacts. */
30
+ include?: string[]
31
+ /** Tables omitted from generated NSDB artifacts. Mutually exclusive with include. */
32
+ exclude?: string[]
33
+ /** Per-table client exposure rules. Unspecified columns keep inferred defaults. */
34
+ columns?: Record<string, Record<string, {
35
+ selectable?: boolean
36
+ editable?: boolean
37
+ hidden?: boolean
38
+ serverOnly?: boolean
39
+ }>>
40
+ }
41
+ templates?: {
42
+ model?: string
43
+ schema?: string
44
+ useNsdbModel?: string
45
+ store?: string
46
+ }
47
+ generators?: {
48
+ force?: boolean
49
+ }
50
+ }
@@ -0,0 +1,66 @@
1
+ export type FieldType =
2
+ | 'text'
3
+ | 'textarea'
4
+ | 'select'
5
+ | 'checkbox'
6
+ | 'number'
7
+ | 'date'
8
+ | 'datetime'
9
+ | 'json'
10
+ | 'array'
11
+ | 'file'
12
+ | 'relation'
13
+
14
+ export type EntityField = {
15
+ label: string
16
+ type: FieldType
17
+ required?: boolean
18
+ readonly?: boolean
19
+ selectable?: boolean
20
+ editable?: boolean
21
+ insertable?: boolean
22
+ updatable?: boolean
23
+ hidden?: boolean
24
+ serverOnly?: boolean
25
+ primaryKey?: boolean
26
+ nullable?: boolean
27
+ hasDefault?: boolean
28
+ databaseType?: string
29
+ defaultExpression?: string | null
30
+ default?: any
31
+ options?: Array<{ label: string; value: any }>
32
+ relation?: EntityRelation
33
+ }
34
+
35
+ export type RelationKind = 'belongsTo' | 'hasOne' | 'hasMany' | 'manyToMany'
36
+
37
+ export interface EntityRelation {
38
+ alias?: string
39
+ kind: RelationKind
40
+ direction?: 'forward' | 'inverse' | 'through'
41
+ nullable?: boolean
42
+ composite?: boolean
43
+ throughTable?: string
44
+ /** Table référencée dans Supabase (ex: "playlists") */
45
+ referencedTable: string
46
+ embedResource?: string
47
+ /** Colonnes locales qui forment la FK (souvent ["playlist_id"]) */
48
+ localColumns: string[]
49
+ /** Colonnes référencées (souvent ["id"]) */
50
+ referencedColumns: string[]
51
+ /** Nom de la FK dans Supabase (facultatif, pour debug) */
52
+ foreignKeyName?: string,
53
+
54
+ /**
55
+ * Champ de la table référencée à utiliser comme label.
56
+ * Exemple: "title", "username", "full_name", etc.
57
+ * Si non fourni, on tombera sur 'id' (sans guess "intelligent").
58
+ */
59
+ displayField?: string
60
+
61
+ /**
62
+ * Est-ce qu'on autorise la création inline de l'entité liée
63
+ * dans NsdbForm ? (préparation pour la suite)
64
+ */
65
+ allowInlineCreate?: boolean
66
+ }
package/types/index.ts ADDED
@@ -0,0 +1,14 @@
1
+ export type { NsdbConfig } from './config'
2
+ export type { EntityField, EntityRelation, FieldType, RelationKind } from './entities'
3
+ export type {
4
+ Column,
5
+ ListOptions,
6
+ NsdbTableClasses,
7
+ OrderDirection,
8
+ SortState,
9
+ WhereClause,
10
+ WhereOperator,
11
+ WherePrimitive,
12
+ WhereValue,
13
+ } from './list'
14
+ export type { ModelHandle, ModelMutationTarget, ModelQuery, ModelWhere } from './model'