@indexeddb-orm/idb-orm 0.0.1

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 (216) hide show
  1. package/.vscode/extensions.json +5 -0
  2. package/README.md +1280 -0
  3. package/angular-demo-app/README.md +84 -0
  4. package/angular-demo-app/angular.json +109 -0
  5. package/angular-demo-app/package-lock.json +14215 -0
  6. package/angular-demo-app/package.json +41 -0
  7. package/angular-demo-app/src/app/app.component.ts +481 -0
  8. package/angular-demo-app/src/app/app.routes.ts +8 -0
  9. package/angular-demo-app/src/app/components/actions.component.ts +202 -0
  10. package/angular-demo-app/src/app/components/cloud-sync-demo.component.ts +296 -0
  11. package/angular-demo-app/src/app/components/live-query-demo.component.ts +307 -0
  12. package/angular-demo-app/src/app/components/main-info.component.ts +148 -0
  13. package/angular-demo-app/src/app/components/posts-live-query-demo.component.ts +336 -0
  14. package/angular-demo-app/src/app/components/typescript-demo.component.ts +268 -0
  15. package/angular-demo-app/src/entities/post-tag.entity.ts +25 -0
  16. package/angular-demo-app/src/entities/post.entity.ts +49 -0
  17. package/angular-demo-app/src/entities/profile.entity.ts +42 -0
  18. package/angular-demo-app/src/entities/tag.entity.ts +36 -0
  19. package/angular-demo-app/src/entities/user.entity.ts +59 -0
  20. package/angular-demo-app/src/favicon.ico +1 -0
  21. package/angular-demo-app/src/index.html +16 -0
  22. package/angular-demo-app/src/main.ts +13 -0
  23. package/angular-demo-app/src/services/app-logic.service.ts +449 -0
  24. package/angular-demo-app/src/services/cloud-sync.service.ts +95 -0
  25. package/angular-demo-app/src/services/database.service.ts +26 -0
  26. package/angular-demo-app/src/services/live-query.service.ts +63 -0
  27. package/angular-demo-app/src/services/posts-live-query.service.ts +86 -0
  28. package/angular-demo-app/src/services/typescript-demo.service.ts +59 -0
  29. package/angular-demo-app/src/styles.scss +50 -0
  30. package/angular-demo-app/tsconfig.app.json +13 -0
  31. package/angular-demo-app/tsconfig.json +34 -0
  32. package/angular-demo-app/tsconfig.spec.json +13 -0
  33. package/dist/Database.d.ts +206 -0
  34. package/dist/Database.js +288 -0
  35. package/dist/decorators/Column.d.ts +79 -0
  36. package/dist/decorators/Column.js +236 -0
  37. package/dist/decorators/Entity.d.ts +32 -0
  38. package/dist/decorators/Entity.js +44 -0
  39. package/dist/decorators/Relation.d.ts +70 -0
  40. package/dist/decorators/Relation.js +120 -0
  41. package/dist/decorators/index.d.ts +3 -0
  42. package/dist/decorators/index.js +3 -0
  43. package/dist/errors/ValidationError.d.ts +4 -0
  44. package/dist/errors/ValidationError.js +8 -0
  45. package/dist/index.d.ts +8 -0
  46. package/dist/index.js +7 -0
  47. package/dist/metadata/Column.d.ts +8 -0
  48. package/dist/metadata/Column.js +44 -0
  49. package/dist/metadata/Entity.d.ts +11 -0
  50. package/dist/metadata/Entity.js +21 -0
  51. package/dist/metadata/Relation.d.ts +20 -0
  52. package/dist/metadata/Relation.js +74 -0
  53. package/dist/metadata/index.d.ts +3 -0
  54. package/dist/metadata/index.js +3 -0
  55. package/dist/services/AggregationService.d.ts +38 -0
  56. package/dist/services/AggregationService.js +229 -0
  57. package/dist/services/BaseEntity.d.ts +32 -0
  58. package/dist/services/BaseEntity.js +62 -0
  59. package/dist/services/CloudSyncService.d.ts +100 -0
  60. package/dist/services/CloudSyncService.js +196 -0
  61. package/dist/services/DecoratorUtils.d.ts +12 -0
  62. package/dist/services/DecoratorUtils.js +10 -0
  63. package/dist/services/EntityFactory.d.ts +25 -0
  64. package/dist/services/EntityFactory.js +27 -0
  65. package/dist/services/EntityRegistry.d.ts +61 -0
  66. package/dist/services/EntityRegistry.js +56 -0
  67. package/dist/services/EntitySchema.d.ts +56 -0
  68. package/dist/services/EntitySchema.js +125 -0
  69. package/dist/services/MigrationManager.d.ts +70 -0
  70. package/dist/services/MigrationManager.js +181 -0
  71. package/dist/services/RelationLoader.d.ts +66 -0
  72. package/dist/services/RelationLoader.js +310 -0
  73. package/dist/services/SchemaBuilder.d.ts +68 -0
  74. package/dist/services/SchemaBuilder.js +191 -0
  75. package/dist/services/index.d.ts +7 -0
  76. package/dist/services/index.js +7 -0
  77. package/dist/types.d.ts +152 -0
  78. package/dist/types.js +1 -0
  79. package/dist/utils/logger.d.ts +12 -0
  80. package/dist/utils/logger.js +16 -0
  81. package/eslint.config.js +49 -0
  82. package/homepage/favicon.svg +36 -0
  83. package/homepage/index.html +1725 -0
  84. package/package.json +78 -0
  85. package/react-demo-app/README.md +61 -0
  86. package/react-demo-app/eslint.config.js +60 -0
  87. package/react-demo-app/index.html +13 -0
  88. package/react-demo-app/package-lock.json +4955 -0
  89. package/react-demo-app/package.json +39 -0
  90. package/react-demo-app/src/App.tsx +172 -0
  91. package/react-demo-app/src/assets/react.svg +1 -0
  92. package/react-demo-app/src/components/Actions.tsx +171 -0
  93. package/react-demo-app/src/components/CloudSyncDemo.tsx +191 -0
  94. package/react-demo-app/src/components/LiveQueryDemo.tsx +122 -0
  95. package/react-demo-app/src/components/MainInfo.tsx +75 -0
  96. package/react-demo-app/src/components/PostsLiveQueryDemo.tsx +185 -0
  97. package/react-demo-app/src/components/TypeScriptDemo.tsx +190 -0
  98. package/react-demo-app/src/database/Database.ts +30 -0
  99. package/react-demo-app/src/entities/Post.ts +48 -0
  100. package/react-demo-app/src/entities/PostTag.ts +26 -0
  101. package/react-demo-app/src/entities/Profile.ts +41 -0
  102. package/react-demo-app/src/entities/Tag.ts +35 -0
  103. package/react-demo-app/src/entities/User.ts +61 -0
  104. package/react-demo-app/src/hooks/useAppLogic.ts +565 -0
  105. package/react-demo-app/src/hooks/useCloudSyncDemo.ts +84 -0
  106. package/react-demo-app/src/hooks/useLiveQueryDemo.ts +68 -0
  107. package/react-demo-app/src/hooks/usePostsLiveQueryDemo.ts +64 -0
  108. package/react-demo-app/src/hooks/useTypeScriptDemo.ts +43 -0
  109. package/react-demo-app/src/index.css +26 -0
  110. package/react-demo-app/src/main.tsx +18 -0
  111. package/react-demo-app/src/migrations/001-add-user-email-index.ts +17 -0
  112. package/react-demo-app/src/migrations/002-add-post-category.ts +37 -0
  113. package/react-demo-app/src/migrations/index.ts +8 -0
  114. package/react-demo-app/src/vite-env.d.ts +1 -0
  115. package/react-demo-app/tsconfig.app.json +22 -0
  116. package/react-demo-app/tsconfig.json +6 -0
  117. package/react-demo-app/vite.config.ts +10 -0
  118. package/src/Database.ts +405 -0
  119. package/src/errors/ValidationError.ts +9 -0
  120. package/src/index.ts +13 -0
  121. package/src/metadata/Column.ts +74 -0
  122. package/src/metadata/Entity.ts +42 -0
  123. package/src/metadata/Relation.ts +121 -0
  124. package/src/metadata/index.ts +5 -0
  125. package/src/services/AggregationService.ts +348 -0
  126. package/src/services/BaseEntity.ts +77 -0
  127. package/src/services/CloudSyncService.ts +248 -0
  128. package/src/services/EntityFactory.ts +35 -0
  129. package/src/services/EntityRegistry.ts +109 -0
  130. package/src/services/EntitySchema.ts +154 -0
  131. package/src/services/MigrationManager.ts +276 -0
  132. package/src/services/RelationLoader.ts +532 -0
  133. package/src/services/SchemaBuilder.ts +237 -0
  134. package/src/services/index.ts +7 -0
  135. package/src/types.d.ts +1 -0
  136. package/src/types.ts +169 -0
  137. package/src/utils/logger.ts +40 -0
  138. package/svelte-demo-app/README.md +61 -0
  139. package/svelte-demo-app/package-lock.json +3000 -0
  140. package/svelte-demo-app/package.json +30 -0
  141. package/svelte-demo-app/src/app.d.ts +12 -0
  142. package/svelte-demo-app/src/app.html +13 -0
  143. package/svelte-demo-app/src/components/Actions.svelte +121 -0
  144. package/svelte-demo-app/src/components/CloudSyncDemo.svelte +333 -0
  145. package/svelte-demo-app/src/components/LiveQueryDemo.svelte +191 -0
  146. package/svelte-demo-app/src/components/MainInfo.svelte +133 -0
  147. package/svelte-demo-app/src/components/PostsLiveQueryDemo.svelte +330 -0
  148. package/svelte-demo-app/src/components/TypeScriptDemo.svelte +251 -0
  149. package/svelte-demo-app/src/database/Database.ts +29 -0
  150. package/svelte-demo-app/src/entities/Post.ts +46 -0
  151. package/svelte-demo-app/src/entities/PostTag.ts +22 -0
  152. package/svelte-demo-app/src/entities/Profile.ts +39 -0
  153. package/svelte-demo-app/src/entities/Tag.ts +33 -0
  154. package/svelte-demo-app/src/entities/User.ts +62 -0
  155. package/svelte-demo-app/src/lib/database/Database.ts +30 -0
  156. package/svelte-demo-app/src/lib/entities/Post.ts +47 -0
  157. package/svelte-demo-app/src/lib/entities/PostTag.ts +23 -0
  158. package/svelte-demo-app/src/lib/entities/Profile.ts +40 -0
  159. package/svelte-demo-app/src/lib/entities/Tag.ts +34 -0
  160. package/svelte-demo-app/src/lib/entities/User.ts +59 -0
  161. package/svelte-demo-app/src/lib/index.ts +7 -0
  162. package/svelte-demo-app/src/lib/migrations/001-add-user-email-index.ts +17 -0
  163. package/svelte-demo-app/src/lib/migrations/002-add-post-category.ts +37 -0
  164. package/svelte-demo-app/src/lib/migrations/index.ts +8 -0
  165. package/svelte-demo-app/src/migrations/001-add-user-email-index.ts +17 -0
  166. package/svelte-demo-app/src/migrations/002-add-post-category.ts +37 -0
  167. package/svelte-demo-app/src/migrations/index.ts +8 -0
  168. package/svelte-demo-app/src/routes/+layout.js +3 -0
  169. package/svelte-demo-app/src/routes/+layout.svelte +228 -0
  170. package/svelte-demo-app/src/routes/+page.js +3 -0
  171. package/svelte-demo-app/src/routes/+page.svelte +1305 -0
  172. package/svelte-demo-app/src/stores/appStore.js +603 -0
  173. package/svelte-demo-app/svelte.config.js +18 -0
  174. package/svelte-demo-app/tsconfig.json +14 -0
  175. package/svelte-demo-app/vite.config.ts +6 -0
  176. package/tests/aggregation.e2e.test.ts +87 -0
  177. package/tests/base-entity.e2e.test.ts +47 -0
  178. package/tests/database-api.e2e.test.ts +177 -0
  179. package/tests/decorators.e2e.test.ts +40 -0
  180. package/tests/entity-schema.e2e.test.ts +58 -0
  181. package/tests/relation-loader-table-names.test.ts +192 -0
  182. package/tests/relations.e2e.test.ts +178 -0
  183. package/tests/zod-runtime.e2e.test.ts +69 -0
  184. package/tsconfig.json +21 -0
  185. package/vitest.config.ts +21 -0
  186. package/vitest.setup.ts +27 -0
  187. package/vue-demo-app/README.md +61 -0
  188. package/vue-demo-app/index.html +13 -0
  189. package/vue-demo-app/package-lock.json +1537 -0
  190. package/vue-demo-app/package.json +27 -0
  191. package/vue-demo-app/src/App.vue +100 -0
  192. package/vue-demo-app/src/components/Actions.vue +135 -0
  193. package/vue-demo-app/src/components/CloudSyncDemo.vue +139 -0
  194. package/vue-demo-app/src/components/LiveQueryDemo.vue +122 -0
  195. package/vue-demo-app/src/components/MainInfo.vue +80 -0
  196. package/vue-demo-app/src/components/PostsLiveQueryDemo.vue +136 -0
  197. package/vue-demo-app/src/components/TypeScriptDemo.vue +133 -0
  198. package/vue-demo-app/src/database/Database.ts +29 -0
  199. package/vue-demo-app/src/entities/Post.ts +48 -0
  200. package/vue-demo-app/src/entities/PostTag.ts +24 -0
  201. package/vue-demo-app/src/entities/Profile.ts +41 -0
  202. package/vue-demo-app/src/entities/Tag.ts +35 -0
  203. package/vue-demo-app/src/entities/User.ts +61 -0
  204. package/vue-demo-app/src/main.ts +29 -0
  205. package/vue-demo-app/src/migrations/001-add-user-email-index.ts +23 -0
  206. package/vue-demo-app/src/migrations/002-add-post-category.ts +46 -0
  207. package/vue-demo-app/src/migrations/index.ts +14 -0
  208. package/vue-demo-app/src/services/useAppLogic.ts +565 -0
  209. package/vue-demo-app/src/services/useCloudSyncDemo.ts +84 -0
  210. package/vue-demo-app/src/services/useLiveQueryDemo.ts +82 -0
  211. package/vue-demo-app/src/services/usePostsLiveQueryDemo.ts +77 -0
  212. package/vue-demo-app/src/services/useTypeScriptDemo.ts +56 -0
  213. package/vue-demo-app/src/vite-env.d.ts +1 -0
  214. package/vue-demo-app/tsconfig.json +25 -0
  215. package/vue-demo-app/tsconfig.node.json +10 -0
  216. package/vue-demo-app/vite.config.ts +16 -0
@@ -0,0 +1,152 @@
1
+ import type Dexie from 'dexie';
2
+ import type { Table } from 'dexie';
3
+ import { z } from 'zod';
4
+ import { BaseEntity } from './services/BaseEntity';
5
+ /**
6
+ * Constructor type for ORM entities.
7
+ */
8
+ export interface EntityConstructor<T extends BaseEntity = BaseEntity> {
9
+ new (): T;
10
+ schema?: z.ZodSchema;
11
+ tableName?: string;
12
+ }
13
+ /**
14
+ * Generic plain object shape for entity instances used in serialization.
15
+ */
16
+ export interface EntityInstance {
17
+ id?: number | string;
18
+ [key: string]: unknown;
19
+ }
20
+ /**
21
+ * Options for an entity column.
22
+ */
23
+ export interface ColumnOptions {
24
+ kind?: 'string' | 'number' | 'boolean' | 'array' | 'object';
25
+ unique?: boolean;
26
+ indexed?: boolean;
27
+ required?: boolean;
28
+ default?: unknown;
29
+ primaryKey?: boolean;
30
+ autoIncrement?: boolean;
31
+ }
32
+ /**
33
+ * Definition of a compound index across multiple columns.
34
+ */
35
+ export interface CompoundIndexOptions {
36
+ name?: string;
37
+ unique?: boolean;
38
+ columns: string[];
39
+ }
40
+ export type RelationType = 'one-to-one' | 'one-to-many' | 'many-to-many';
41
+ /**
42
+ * Configuration for a relation between two entities.
43
+ */
44
+ export interface RelationOptions {
45
+ type: RelationType;
46
+ target: EntityConstructor | string;
47
+ /** Foreign key for one-to-many and many-to-many relations. */
48
+ foreignKey?: string;
49
+ /** Name of the join table for many-to-many relations. */
50
+ joinTable?: string;
51
+ /** If true, related records are deleted automatically. */
52
+ cascade?: boolean;
53
+ /** If true, the relation is auto-loaded with the entity. */
54
+ eager?: boolean;
55
+ }
56
+ /**
57
+ * Internal metadata stored for a relation decorator.
58
+ */
59
+ export interface RelationMetadata extends RelationOptions {
60
+ propertyKey: string;
61
+ }
62
+ /**
63
+ * Options for configuring an entity class.
64
+ */
65
+ export interface EntityOptions {
66
+ tableName?: string;
67
+ schema?: z.ZodSchema;
68
+ timestamps?: boolean;
69
+ }
70
+ /**
71
+ * Result of entity validation.
72
+ */
73
+ export interface ValidationResult {
74
+ isValid: boolean;
75
+ errors: string[];
76
+ }
77
+ /**
78
+ * A single database migration step.
79
+ */
80
+ export interface Migration {
81
+ version: number;
82
+ name: string;
83
+ up: (_db: Dexie) => Promise<void>;
84
+ down?: (_db: Dexie) => Promise<void>;
85
+ }
86
+ /**
87
+ * Configuration for optional cloud synchronization.
88
+ */
89
+ export interface CloudSyncConfig {
90
+ /** Base URL of the synchronization server. */
91
+ databaseUrl: string;
92
+ /** If enabled, queue changes and retry when offline. */
93
+ enableOfflineSupport?: boolean;
94
+ /** Periodic sync interval in milliseconds. */
95
+ syncInterval?: number;
96
+ }
97
+ /**
98
+ * Configuration for creating a Database instance.
99
+ */
100
+ export interface DatabaseConfig {
101
+ /** Name of the IndexedDB database. */
102
+ name: string;
103
+ /** Schema version number. */
104
+ version: number;
105
+ /** List of registered entity classes to include in the schema. */
106
+ entities: EntityConstructor[];
107
+ /** Strategy for handling schema changes. */
108
+ onSchemaChangeStrategy?: 'selective' | 'all';
109
+ /** Optional list of migrations to execute. */
110
+ migrations?: Migration[];
111
+ /** Optional cloud synchronization configuration. */
112
+ cloudSync?: CloudSyncConfig;
113
+ }
114
+ export type TableGetter<T extends BaseEntity> = () => Table<T>;
115
+ /**
116
+ * Options for the aggregation query on a specific entity.
117
+ */
118
+ export interface AggregationOptions<T extends BaseEntity> {
119
+ /** Partial where filter applied to records before aggregation. */
120
+ where?: Partial<T>;
121
+ count?: boolean;
122
+ sum?: (keyof T)[];
123
+ avg?: (keyof T)[];
124
+ min?: (keyof T)[];
125
+ max?: (keyof T)[];
126
+ groupBy?: keyof T;
127
+ include?: string[];
128
+ limit?: number;
129
+ sort?: {
130
+ field: keyof T;
131
+ direction: 'asc' | 'desc';
132
+ };
133
+ }
134
+ /**
135
+ * Result returned by the aggregation service.
136
+ */
137
+ export interface AggregationResult {
138
+ count?: number;
139
+ sum?: Record<string, number>;
140
+ avg?: Record<string, number>;
141
+ min?: Record<string, number>;
142
+ max?: Record<string, number>;
143
+ groups?: Array<{
144
+ key: unknown;
145
+ count: number;
146
+ sum?: Record<string, number>;
147
+ avg?: Record<string, number>;
148
+ min?: Record<string, number>;
149
+ max?: Record<string, number>;
150
+ }>;
151
+ data?: unknown[];
152
+ }
package/dist/types.js ADDED
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,12 @@
1
+ export declare const createServiceLogger: (serviceName: string) => {
2
+ debug: (message: string, ...args: unknown[]) => void;
3
+ info: (message: string, ...args: unknown[]) => void;
4
+ warn: (message: string, ...args: unknown[]) => void;
5
+ error: (message: string, ...args: unknown[]) => void;
6
+ };
7
+ export declare const logger: {
8
+ debug: (message: string, ...args: unknown[]) => void;
9
+ info: (message: string, ...args: unknown[]) => void;
10
+ warn: (message: string, ...args: unknown[]) => void;
11
+ error: (message: string, ...args: unknown[]) => void;
12
+ };
@@ -0,0 +1,16 @@
1
+ import log from 'loglevel';
2
+ const isDevHost = (typeof window !== 'undefined' &&
3
+ typeof window.location !== 'undefined' &&
4
+ (window.location.hostname === 'localhost' ||
5
+ window.location.hostname === '127.0.0.1' ||
6
+ window.location.hostname === '0.0.0.0'));
7
+ log.setLevel(isDevHost ? 'debug' : 'error');
8
+ export const createServiceLogger = (serviceName) => {
9
+ return {
10
+ debug: (message, ...args) => log.debug(`[${serviceName}] ${message}`, ...args),
11
+ info: (message, ...args) => log.info(`[${serviceName}] ${message}`, ...args),
12
+ warn: (message, ...args) => log.warn(`[${serviceName}] ${message}`, ...args),
13
+ error: (message, ...args) => log.error(`[${serviceName}] ${message}`, ...args),
14
+ };
15
+ };
16
+ export const logger = createServiceLogger('indexeddb-orm');
@@ -0,0 +1,49 @@
1
+ import js from '@eslint/js'
2
+ import tseslint from 'typescript-eslint'
3
+ import { globalIgnores } from 'eslint/config'
4
+ import simpleImportSort from 'eslint-plugin-simple-import-sort'
5
+
6
+ export default tseslint.config([
7
+ globalIgnores(['dist']),
8
+ {
9
+ files: ['**/*.ts'],
10
+ extends: [
11
+ js.configs.recommended,
12
+ tseslint.configs.recommended,
13
+ ],
14
+ plugins: {
15
+ 'simple-import-sort': simpleImportSort,
16
+ },
17
+ languageOptions: {
18
+ ecmaVersion: 2020,
19
+ },
20
+ rules: {
21
+ 'semi': ['error', 'always'],
22
+ 'quotes': ['error', 'single', { allowTemplateLiterals: true, avoidEscape: true }],
23
+ 'comma-dangle': ['error', 'always-multiline'],
24
+ 'object-curly-spacing': ['error', 'always'],
25
+ 'array-bracket-spacing': ['error', 'never'],
26
+ 'no-trailing-spaces': 'error',
27
+ 'eol-last': ['error', 'always'],
28
+ 'padding-line-between-statements': [
29
+ 'error',
30
+ { blankLine: 'always', prev: '*', next: 'return' },
31
+ ],
32
+ 'max-len': ['error', { code: 85, tabWidth: 2, ignoreUrls: true, ignoreStrings: true, ignoreTemplateLiterals: false, ignoreComments: false }],
33
+ 'simple-import-sort/imports': 'error',
34
+ 'simple-import-sort/exports': 'error',
35
+ 'object-curly-newline': [
36
+ 'error',
37
+ {
38
+ ImportDeclaration: { multiline: true, minProperties: 2 },
39
+ ExportDeclaration: { multiline: true, minProperties: 2 },
40
+ }
41
+ ],
42
+ // Allow intentionally unused vars prefixed with _
43
+ 'no-unused-vars': ['error', { argsIgnorePattern: '^_', varsIgnorePattern: '^_', caughtErrorsIgnorePattern: '^_' }],
44
+ '@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_', varsIgnorePattern: '^_', caughtErrorsIgnorePattern: '^_' }],
45
+ },
46
+ },
47
+ ])
48
+
49
+
@@ -0,0 +1,36 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 64 64" fill="none">
2
+ <!-- Brand gradient background -->
3
+ <defs>
4
+ <linearGradient id="brand" x1="8" y1="8" x2="56" y2="56" gradientUnits="userSpaceOnUse">
5
+ <stop offset="0%" stop-color="#667eea"/>
6
+ <stop offset="100%" stop-color="#764ba2"/>
7
+ </linearGradient>
8
+ </defs>
9
+
10
+ <rect x="6" y="6" width="52" height="52" rx="14" fill="url(#brand)"/>
11
+
12
+ <!-- Globe icon (centered) -->
13
+ <g stroke="#ffffff" stroke-width="2.6" stroke-linecap="round" stroke-linejoin="round" fill="none">
14
+ <circle cx="32" cy="30" r="14"/>
15
+ <ellipse cx="32" cy="30" rx="9.2" ry="14"/>
16
+ <ellipse cx="32" cy="30" rx="14" ry="9.2"/>
17
+ <path d="M18 30h28"/>
18
+ <path d="M32 16v28"/>
19
+ </g>
20
+
21
+ <!-- Badge: circular, overlapping bottom-right for better composition -->
22
+ <!-- Soft shadow for separation on gradient -->
23
+ <circle cx="46" cy="46" r="10" fill="rgba(0,0,0,0.18)"/>
24
+ <circle cx="45.5" cy="45.5" r="10" fill="#ffffff"/>
25
+
26
+ <!-- Database cylinder inside badge -->
27
+ <g transform="translate(35.5,35.5)">
28
+ <!-- Use brand tint for cylinder to relate with background -->
29
+ <ellipse cx="10" cy="6.2" rx="6.6" ry="3" fill="#6e79ef"/>
30
+ <path d="M3.4 6.2v6.6c0 1.8 2.9 3.3 6.6 3.3s6.6-1.5 6.6-3.3V6.2" fill="#6e79ef"/>
31
+ <path d="M3.4 9.8c0 1.8 2.9 3.3 6.6 3.3s6.6-1.5 6.6-3.3" stroke="#cfd2ff" stroke-width="1.4" fill="none"/>
32
+ </g>
33
+
34
+ <!-- Subtle highlight on card -->
35
+ <circle cx="22" cy="20" r="6" fill="#ffffff" opacity="0.12"/>
36
+ </svg>