@pol-studios/powersync 1.0.34 → 1.0.35

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.
@@ -12,7 +12,8 @@ function normalizeTableSpec(spec) {
12
12
  tableName,
13
13
  schemaName: schemaName2,
14
14
  trackMetadata: false,
15
- alias: toPascalCase(schemaName2) + tableName
15
+ // Initially use just table name - collision detection happens later
16
+ alias: tableName
16
17
  };
17
18
  }
18
19
  return {
@@ -23,12 +24,12 @@ function normalizeTableSpec(spec) {
23
24
  };
24
25
  }
25
26
  const schemaName = spec.schema || "public";
26
- const alias = schemaName === "public" ? spec.table : toPascalCase(schemaName) + spec.table;
27
27
  return {
28
28
  tableName: spec.table,
29
29
  schemaName,
30
30
  trackMetadata: spec.trackMetadata ?? false,
31
- alias
31
+ // Initially use just table name - collision detection happens later
32
+ alias: spec.table
32
33
  };
33
34
  }
34
35
  function toPascalCase(str) {
@@ -82,8 +83,26 @@ function generatePowerSyncSchema(databaseSchema, tables) {
82
83
  const warnings = [];
83
84
  const tableDefinitions = {};
84
85
  const tableMap = /* @__PURE__ */ new Map();
86
+ const tableNameCounts = /* @__PURE__ */ new Map();
87
+ const normalizedSpecs = [];
85
88
  for (const spec of tables) {
86
89
  const normalized = normalizeTableSpec(spec);
90
+ normalizedSpecs.push(normalized);
91
+ const count = tableNameCounts.get(normalized.tableName) || 0;
92
+ tableNameCounts.set(normalized.tableName, count + 1);
93
+ }
94
+ const collidingTableNames = /* @__PURE__ */ new Set();
95
+ for (const [tableName, count] of tableNameCounts) {
96
+ if (count > 1) {
97
+ collidingTableNames.add(tableName);
98
+ }
99
+ }
100
+ for (const normalized of normalizedSpecs) {
101
+ if (collidingTableNames.has(normalized.tableName)) {
102
+ normalized.alias = toPascalCase(normalized.schemaName) + normalized.tableName;
103
+ }
104
+ }
105
+ for (const normalized of normalizedSpecs) {
87
106
  const {
88
107
  tableName,
89
108
  schemaName,
@@ -217,4 +236,4 @@ export {
217
236
  getTableStrategy,
218
237
  isTablePowerSync
219
238
  };
220
- //# sourceMappingURL=chunk-HRAVPIAZ.js.map
239
+ //# sourceMappingURL=chunk-RLQOUNAG.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/config/schema-generator.ts","../src/config/index.ts"],"sourcesContent":["/**\n * Schema Generator for @pol-studios/powersync\n *\n * Generates PowerSync Schema at runtime from the Supabase database schema object.\n * This eliminates the need to manually maintain a separate PowerSync schema file.\n *\n * Type Mapping:\n * - number -> column.real (ALL numbers use real, no integer/real distinction)\n * - boolean -> column.integer (SQLite stores booleans as 0/1)\n * - string -> column.text\n * - date -> column.text (ISO strings)\n * - enum -> column.text\n * - json -> SKIPPED (not supported in PowerSync)\n * - arrays -> SKIPPED (not supported in PowerSync)\n *\n * Skipped columns:\n * - `id` (PowerSync handles this automatically)\n * - JSON types (complex objects not supported)\n * - Array types (not supported)\n */\n\nimport { column, Schema, Table } from '@powersync/react-native';\nimport type { DatabaseSchemaObject, DatabaseTableDef, DatabaseColumnDef, TableSpec } from './types';\nimport { LOCAL_ONLY_TABLES } from '../sync/local-tables';\n\n// Type alias for PowerSync column types (same as what column.real/integer/text return)\ntype PowerSyncColumn = typeof column.real | typeof column.integer | typeof column.text;\n\n// ─── Table Spec Normalization ─────────────────────────────────────────────────\n\n/**\n * Normalized table specification.\n */\ninterface NormalizedTableSpec {\n /** Table name (without schema prefix) */\n tableName: string;\n /** Schema name */\n schemaName: string;\n /** Whether to track sync metadata */\n trackMetadata: boolean;\n /**\n * Alias for PowerSync table name.\n *\n * Collision-aware aliasing:\n * - If table name is unique across all schemas → use just the table name (e.g., \"Profile\")\n * - If table name collides with another schema → use SchemaTable format (e.g., \"CoreProfile\")\n *\n * Note: The alias is initially set to the table name by normalizeTableSpec.\n * The generatePowerSyncSchema function then updates aliases for colliding tables.\n */\n alias: string;\n}\n\n/**\n * Normalize a TableSpec to a consistent format.\n *\n * Note: This function sets the alias to just the table name initially.\n * Collision detection and prefix assignment happens in generatePowerSyncSchema,\n * which has visibility into all tables and can detect naming collisions.\n *\n * @param spec - The table specification (string or object)\n * @returns Normalized specification with all fields filled\n */\nexport function normalizeTableSpec(spec: TableSpec): NormalizedTableSpec {\n if (typeof spec === 'string') {\n // Check for schema prefix (e.g., \"core.Profile\")\n if (spec.includes('.')) {\n const [schemaName, tableName] = spec.split('.');\n return {\n tableName,\n schemaName,\n trackMetadata: false,\n // Initially use just table name - collision detection happens later\n alias: tableName\n };\n }\n // Simple table name - public schema\n return {\n tableName: spec,\n schemaName: 'public',\n trackMetadata: false,\n alias: spec\n };\n }\n\n // Object format\n const schemaName = spec.schema || 'public';\n return {\n tableName: spec.table,\n schemaName,\n trackMetadata: spec.trackMetadata ?? false,\n // Initially use just table name - collision detection happens later\n alias: spec.table\n };\n}\n\n/**\n * Convert a string to PascalCase.\n */\nfunction toPascalCase(str: string): string {\n return str.charAt(0).toUpperCase() + str.slice(1);\n}\n\n// ─── Type Mapping ─────────────────────────────────────────────────────────────\n\n/**\n * Map a database column type to a PowerSync column type.\n *\n * @param col - The column definition\n * @returns PowerSync column type or null if should be skipped\n */\nfunction mapColumnType(col: DatabaseColumnDef): PowerSyncColumn | null {\n const type = col.type;\n\n // Skip JSON types - not supported in PowerSync\n if (type === 'json') {\n return null;\n }\n\n // Map based on type\n switch (type) {\n case 'number':\n // ALL numbers use column.real - no heuristics\n return column.real;\n case 'boolean':\n // SQLite stores booleans as integers (0/1)\n return column.integer;\n case 'string':\n case 'date':\n case 'enum':\n return column.text;\n default:\n // Unknown types default to text\n return column.text;\n }\n}\n\n/**\n * Check if a column should be skipped.\n *\n * @param col - The column definition\n * @returns true if the column should be skipped\n */\nfunction shouldSkipColumn(col: DatabaseColumnDef): boolean {\n // Skip `id` - PowerSync handles this automatically\n if (col.name === 'id') {\n return true;\n }\n\n // Skip JSON types\n if (col.type === 'json') {\n return true;\n }\n\n // Skip array-like columns (columns ending with 's' that are JSON)\n // This is a heuristic - actual array detection would need type info\n // For now, we rely on the type being 'json' which is already skipped\n\n return false;\n}\n\n// ─── Table Lookup ─────────────────────────────────────────────────────────────\n\n/**\n * Get a table definition from the database schema.\n *\n * @param databaseSchema - The full database schema object\n * @param schemaName - The schema name (e.g., \"public\", \"core\")\n * @param tableName - The table name\n * @returns The table definition or null if not found\n */\nexport function getTableFromSchema(databaseSchema: DatabaseSchemaObject, schemaName: string, tableName: string): DatabaseTableDef | null {\n const schema = databaseSchema.schemas[schemaName];\n if (!schema) {\n console.warn(`[PolConfig] Schema \"${schemaName}\" not found in databaseSchema`);\n return null;\n }\n const table = schema.tables[tableName];\n if (!table) {\n // Also check views\n const view = schema.views?.[tableName];\n if (view) {\n return view;\n }\n console.warn(`[PolConfig] Table \"${schemaName}.${tableName}\" not found in databaseSchema`);\n return null;\n }\n return table;\n}\n\n// ─── Schema Generation ────────────────────────────────────────────────────────\n\n/**\n * Result of schema generation.\n */\nexport interface SchemaGenerationResult {\n /** The generated PowerSync Schema */\n schema: Schema;\n /** Warnings encountered during generation */\n warnings: string[];\n /** Map of table alias to normalized spec */\n tableMap: Map<string, NormalizedTableSpec>;\n}\n\n/**\n * Generate a PowerSync Schema from the database schema and table specs.\n *\n * This function implements collision-aware alias generation:\n * 1. First pass: normalize all specs and detect table name collisions\n * 2. Second pass: assign aliases - only add schema prefix for colliding names\n *\n * @param databaseSchema - The database schema object (from generated types)\n * @param tables - Array of table specifications to sync\n * @returns The generated PowerSync Schema and metadata\n *\n * @example\n * ```typescript\n * const { schema, warnings } = generatePowerSyncSchema(databaseSchema, [\n * \"Project\",\n * \"Task\",\n * { table: \"Profile\", schema: \"core\" },\n * ]);\n *\n * // Use the schema with PowerSyncProvider\n * <PowerSyncProvider config={{ schema, ... }} />\n * ```\n */\nexport function generatePowerSyncSchema(databaseSchema: DatabaseSchemaObject, tables: TableSpec[]): SchemaGenerationResult {\n const warnings: string[] = [];\n const tableDefinitions: Record<string, Table> = {};\n const tableMap = new Map<string, NormalizedTableSpec>();\n\n // ─── First Pass: Normalize specs and detect collisions ─────────────────────\n // Count how many times each table name appears across all schemas.\n // Table names that appear more than once need schema prefixes to disambiguate.\n const tableNameCounts = new Map<string, number>();\n const normalizedSpecs: NormalizedTableSpec[] = [];\n for (const spec of tables) {\n const normalized = normalizeTableSpec(spec);\n normalizedSpecs.push(normalized);\n\n // Track table name occurrences for collision detection\n const count = tableNameCounts.get(normalized.tableName) || 0;\n tableNameCounts.set(normalized.tableName, count + 1);\n }\n\n // Build set of colliding table names (names that appear more than once)\n const collidingTableNames = new Set<string>();\n for (const [tableName, count] of tableNameCounts) {\n if (count > 1) {\n collidingTableNames.add(tableName);\n }\n }\n\n // ─── Second Pass: Assign collision-aware aliases ───────────────────────────\n // Update aliases for tables with colliding names to use SchemaTable format.\n for (const normalized of normalizedSpecs) {\n if (collidingTableNames.has(normalized.tableName)) {\n // Collision detected: use SchemaTable format (e.g., \"CoreProfile\")\n normalized.alias = toPascalCase(normalized.schemaName) + normalized.tableName;\n }\n // Non-colliding tables keep their alias as just the table name\n }\n\n // ─── Third Pass: Generate PowerSync tables ─────────────────────────────────\n for (const normalized of normalizedSpecs) {\n const {\n tableName,\n schemaName,\n alias\n } = normalized;\n\n // Get table definition from schema\n const tableDef = getTableFromSchema(databaseSchema, schemaName, tableName);\n if (!tableDef) {\n warnings.push(`Table \"${schemaName}.${tableName}\" not found, skipping`);\n continue;\n }\n\n // Build PowerSync columns\n const columns: Record<string, PowerSyncColumn> = {};\n let columnCount = 0;\n for (const col of tableDef.columns) {\n // Skip certain columns\n if (shouldSkipColumn(col)) {\n continue;\n }\n\n // Map the column type\n const psType = mapColumnType(col);\n if (psType === null) {\n // Column type not supported, skip with warning\n warnings.push(`Column \"${tableName}.${col.name}\" has unsupported type \"${col.type}\", skipping`);\n continue;\n }\n columns[col.name] = psType;\n columnCount++;\n }\n\n // Create PowerSync Table\n if (columnCount > 0) {\n // Check for duplicate tables\n if (tableDefinitions[alias]) {\n warnings.push(`Duplicate table \"${alias}\" - later definition wins`);\n }\n\n // Pass trackMetadata option if specified\n tableDefinitions[alias] = normalized.trackMetadata ? new Table(columns, {\n trackMetadata: true\n }) : new Table(columns);\n tableMap.set(alias, normalized);\n } else {\n warnings.push(`Table \"${schemaName}.${tableName}\" has no valid columns after filtering, skipping`);\n }\n }\n\n // Add local-only tables for sync status persistence\n const allTableDefinitions = {\n ...tableDefinitions,\n ...LOCAL_ONLY_TABLES\n };\n\n // Create the Schema\n const schema = new Schema(allTableDefinitions);\n return {\n schema,\n warnings,\n tableMap\n };\n}\n\n// ─── Schema Router ────────────────────────────────────────────────────────────\n\n/**\n * Create a schema router function from the table map.\n *\n * @param tableMap - Map of table alias to normalized spec\n * @returns A function that returns the schema for a given table\n */\nexport function createSchemaRouter(tableMap: Map<string, NormalizedTableSpec>): (table: string) => string {\n return (table: string): string => {\n // Check if it's a known PowerSync table\n const spec = tableMap.get(table);\n if (spec) {\n return spec.schemaName;\n }\n\n // Check for schema-prefixed format (e.g., \"core.Profile\")\n if (table.includes('.')) {\n const [schema] = table.split('.');\n return schema;\n }\n\n // Default to public\n return 'public';\n };\n}","/**\n * Unified Configuration for @pol-studios/powersync\n *\n * This module provides the `definePolConfig` function for creating\n * a unified configuration that automatically generates:\n * - PowerSync Schema from the database schema\n * - Table routing strategies for DataLayer\n * - Attachment queue configuration\n *\n * @example\n * ```typescript\n * import { definePolConfig } from '@pol-studios/powersync/config';\n * import { databaseSchema } from './databaseSchema';\n *\n * export const polConfig = definePolConfig({\n * powerSyncUrl: process.env.EXPO_PUBLIC_POWERSYNC_URL,\n * databaseSchema,\n * powersync: [\"Project\", \"Task\", { table: \"Profile\", schema: \"core\" }],\n * attachments: {\n * source: { type: 'supabase-bucket', bucket: 'photos' },\n * watchPaths: (db, supabase, onUpdate) => {\n * const abort = new AbortController();\n * db.watch(\n * `SELECT storagePath FROM photos WHERE storagePath IS NOT NULL`,\n * [],\n * { onResult: (r) => onUpdate(r.rows._array.map(x => x.storagePath)) },\n * { signal: abort.signal }\n * );\n * return () => abort.abort();\n * },\n * },\n * });\n *\n * // Use with OfflineDataProvider\n * <OfflineDataProvider polConfig={polConfig}>\n * <App />\n * </OfflineDataProvider>\n * ```\n */\n\nimport type { PolConfig, ProcessedPolConfig, ProcessedTableStrategy, TableSpec } from './types';\nimport { generatePowerSyncSchema, normalizeTableSpec, createSchemaRouter } from './schema-generator';\n\n// Re-export types\nexport type { PolConfig, ProcessedPolConfig, TableSpec, ProcessedTableStrategy, PolConfigAttachments, PolConfigConnector, PolConfigSync, DatabaseSchemaObject, DatabaseTableDef, DatabaseColumnDef,\n// Alias for attachment source type from config perspective\nPolAttachmentSource } from './types';\n\n// Re-export attachment source types from attachments module\n// (these are the canonical definitions)\nexport type { AttachmentSource, SupabaseBucketSource, CustomAttachmentSource } from '../attachments/types';\n\n// Re-export schema generator utilities\nexport { generatePowerSyncSchema, normalizeTableSpec, createSchemaRouter, getTableFromSchema } from './schema-generator';\n\n// ─── Configuration Processing ─────────────────────────────────────────────────\n\n/**\n * Create a unified PowerSync configuration.\n *\n * This function processes the configuration to generate:\n * - `__generatedSchema`: PowerSync Schema ready for use\n * - `__tableStrategies`: Lookup map for routing queries\n * - `__schemaRouter`: Function to resolve table schemas\n *\n * @param config - The unified configuration\n * @returns Processed configuration with generated artifacts\n *\n * @example\n * ```typescript\n * const polConfig = definePolConfig({\n * powerSyncUrl: 'https://your-instance.powersync.journeyapps.com',\n * databaseSchema,\n * powersync: [\n * \"Project\",\n * \"Task\",\n * { table: \"Profile\", schema: \"core\" },\n * ],\n * });\n *\n * // Access generated schema\n * console.log(polConfig.__generatedSchema);\n *\n * // Check if a table uses PowerSync\n * const strategy = polConfig.__tableStrategies[\"Project\"];\n * console.log(strategy.strategy); // \"powersync\"\n * ```\n */\nexport function definePolConfig(config: PolConfig): ProcessedPolConfig {\n // Validate required fields\n if (!config.databaseSchema) {\n throw new Error('[definePolConfig] databaseSchema is required');\n }\n if (!config.powersync || config.powersync.length === 0) {\n throw new Error('[definePolConfig] powersync array must contain at least one table');\n }\n\n // Generate PowerSync schema\n const {\n schema,\n warnings,\n tableMap\n } = generatePowerSyncSchema(config.databaseSchema, config.powersync);\n\n // Log warnings in development (safe check for non-React-Native environments)\n if (warnings.length > 0 && typeof __DEV__ !== 'undefined' && __DEV__) {\n console.warn('[definePolConfig] Schema generation warnings:', warnings);\n }\n\n // Validate that at least one table was successfully generated\n if (tableMap.size === 0) {\n throw new Error('[definePolConfig] No valid tables found in powersync array. ' + `Check that the tables exist in databaseSchema. Warnings: ${warnings.join(', ')}`);\n }\n\n // Build table strategies lookup\n const tableStrategies: Record<string, ProcessedTableStrategy> = {};\n const powersyncTables: string[] = [];\n for (const [alias, spec] of tableMap.entries()) {\n // Add to strategies map with alias as key\n tableStrategies[alias] = {\n strategy: 'powersync',\n schema: spec.schemaName,\n table: spec.tableName,\n alias: alias // PowerSync SQLite table name\n };\n\n // Also add with full qualified name for lookup\n const qualifiedName = spec.schemaName === 'public' ? spec.tableName : `${spec.schemaName}.${spec.tableName}`;\n if (qualifiedName !== alias) {\n tableStrategies[qualifiedName] = {\n strategy: 'powersync',\n schema: spec.schemaName,\n table: spec.tableName,\n alias: alias // PowerSync SQLite table name (same alias for both keys)\n };\n }\n powersyncTables.push(qualifiedName);\n }\n\n // Create schema router\n const schemaRouter = createSchemaRouter(tableMap);\n\n // Return processed config\n const processedConfig: ProcessedPolConfig = {\n ...config,\n __generatedSchema: schema,\n __tableStrategies: tableStrategies,\n __schemaRouter: schemaRouter,\n __powersyncTables: powersyncTables\n };\n return processedConfig;\n}\n\n// ─── Helper Functions ─────────────────────────────────────────────────────────\n\n/**\n * Check if a configuration is a processed PolConfig.\n *\n * @param config - The configuration to check\n * @returns true if the config has been processed by definePolConfig\n */\nexport function isProcessedPolConfig(config: unknown): config is ProcessedPolConfig {\n return typeof config === 'object' && config !== null && '__generatedSchema' in config && '__tableStrategies' in config;\n}\n\n/**\n * Get the strategy for a table from a processed config.\n *\n * @param config - The processed configuration\n * @param tableName - Table name (can be \"Table\" or \"schema.Table\")\n * @returns The strategy or undefined if not found\n */\nexport function getTableStrategy(config: ProcessedPolConfig, tableName: string): ProcessedTableStrategy | undefined {\n return config.__tableStrategies[tableName];\n}\n\n/**\n * Check if a table uses PowerSync for sync.\n *\n * @param config - The processed configuration\n * @param tableName - Table name\n * @returns true if the table is synced via PowerSync\n */\nexport function isTablePowerSync(config: ProcessedPolConfig, tableName: string): boolean {\n const strategy = getTableStrategy(config, tableName);\n return strategy?.strategy === 'powersync';\n}\n\n// ─── Global Declaration ───────────────────────────────────────────────────────\n\n// Declare __DEV__ global for React Native\ndeclare const __DEV__: boolean;"],"mappings":"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","names":["schemaName"]}
@@ -1,5 +1,5 @@
1
- import { D as DatabaseSchemaObject, T as TableSpec, f as DatabaseTableDef, P as PolConfig, a as ProcessedPolConfig, b as ProcessedTableStrategy } from '../types-BhAEsJj-.js';
2
- export { g as DatabaseColumnDef, c as PolConfigAttachments, d as PolConfigConnector, e as PolConfigSync } from '../types-BhAEsJj-.js';
1
+ import { D as DatabaseSchemaObject, T as TableSpec, f as DatabaseTableDef, P as PolConfig, a as ProcessedPolConfig, b as ProcessedTableStrategy } from '../types-DIp7AoIe.js';
2
+ export { g as DatabaseColumnDef, c as PolConfigAttachments, d as PolConfigConnector, e as PolConfigSync } from '../types-DIp7AoIe.js';
3
3
  export { A as AttachmentSource, C as CustomAttachmentSource, A as PolAttachmentSource, b as SupabaseBucketSource } from '../types-JCEhw2Lf.js';
4
4
  import { Schema } from '@powersync/react-native';
5
5
  import '../types-DqJnP50o.js';
@@ -41,14 +41,23 @@ interface NormalizedTableSpec {
41
41
  trackMetadata: boolean;
42
42
  /**
43
43
  * Alias for PowerSync table name.
44
- * For cross-schema tables, this is SchemaTable (e.g., "CoreProfile").
45
- * For public schema, this is just the table name.
44
+ *
45
+ * Collision-aware aliasing:
46
+ * - If table name is unique across all schemas → use just the table name (e.g., "Profile")
47
+ * - If table name collides with another schema → use SchemaTable format (e.g., "CoreProfile")
48
+ *
49
+ * Note: The alias is initially set to the table name by normalizeTableSpec.
50
+ * The generatePowerSyncSchema function then updates aliases for colliding tables.
46
51
  */
47
52
  alias: string;
48
53
  }
49
54
  /**
50
55
  * Normalize a TableSpec to a consistent format.
51
56
  *
57
+ * Note: This function sets the alias to just the table name initially.
58
+ * Collision detection and prefix assignment happens in generatePowerSyncSchema,
59
+ * which has visibility into all tables and can detect naming collisions.
60
+ *
52
61
  * @param spec - The table specification (string or object)
53
62
  * @returns Normalized specification with all fields filled
54
63
  */
@@ -76,6 +85,10 @@ interface SchemaGenerationResult {
76
85
  /**
77
86
  * Generate a PowerSync Schema from the database schema and table specs.
78
87
  *
88
+ * This function implements collision-aware alias generation:
89
+ * 1. First pass: normalize all specs and detect table name collisions
90
+ * 2. Second pass: assign aliases - only add schema prefix for colliding names
91
+ *
79
92
  * @param databaseSchema - The database schema object (from generated types)
80
93
  * @param tables - Array of table specifications to sync
81
94
  * @returns The generated PowerSync Schema and metadata
@@ -7,7 +7,7 @@ import {
7
7
  isProcessedPolConfig,
8
8
  isTablePowerSync,
9
9
  normalizeTableSpec
10
- } from "../chunk-HRAVPIAZ.js";
10
+ } from "../chunk-RLQOUNAG.js";
11
11
  import "../chunk-OGUFUZSY.js";
12
12
  import "../chunk-5WRI5ZAA.js";
13
13
  export {
package/dist/index.d.ts CHANGED
@@ -23,7 +23,7 @@ export { AttachmentQueueContextValue, useAttachmentQueueContext, usePowerSyncCon
23
23
  export { AsyncStorageAdapter, CompressedImage, CompressionOptions, ConnectionType, DatabaseOptions, FileInfo, FileSystemAdapter, ImageProcessorAdapter, LoggerAdapter, NetworkAdapter, PlatformAdapter, PlatformType, detectPlatform } from './platform/index.js';
24
24
  export { ATTACHMENT_TABLE, AbstractAttachmentQueue, AttachmentState, AttachmentTable, AttachmentTableOptions, AttachmentQueueOptions as BaseAttachmentQueueOptions, DEFAULT_ATTACHMENT_QUEUE_OPTIONS, EncodingType, AttachmentRecord as OfficialAttachmentRecord, StorageAdapter } from '@powersync/attachments';
25
25
  export { AbstractPowerSyncDatabase as PowerSyncDBInterface } from '@powersync/common';
26
- export { g as DatabaseColumnDef, D as DatabaseSchemaObject, f as DatabaseTableDef, P as PolConfig, c as PolConfigAttachments, d as PolConfigConnector, e as PolConfigSync, a as ProcessedPolConfig, b as ProcessedTableStrategy, T as TableSpec } from './types-BhAEsJj-.js';
26
+ export { g as DatabaseColumnDef, D as DatabaseSchemaObject, f as DatabaseTableDef, P as PolConfig, c as PolConfigAttachments, d as PolConfigConnector, e as PolConfigSync, a as ProcessedPolConfig, b as ProcessedTableStrategy, T as TableSpec } from './types-DIp7AoIe.js';
27
27
  import '@powersync/react-native';
28
28
  import 'react';
29
29
  import '@tanstack/react-query';
package/dist/index.js CHANGED
@@ -13,7 +13,7 @@ import {
13
13
  isProcessedPolConfig,
14
14
  isTablePowerSync,
15
15
  normalizeTableSpec
16
- } from "./chunk-HRAVPIAZ.js";
16
+ } from "./chunk-RLQOUNAG.js";
17
17
  import {
18
18
  DOWNLOAD_WORKFLOW_STATES,
19
19
  STATE_MAPPING,
@@ -23,7 +23,7 @@ export { createNativePlatformAdapter } from './platform/index.native.js';
23
23
  export { OfflineDataProvider } from './provider/index.native.js';
24
24
  export { ATTACHMENT_TABLE, AbstractAttachmentQueue, AttachmentState, AttachmentTable, AttachmentTableOptions, AttachmentQueueOptions as BaseAttachmentQueueOptions, DEFAULT_ATTACHMENT_QUEUE_OPTIONS, EncodingType, AttachmentRecord as OfficialAttachmentRecord, StorageAdapter } from '@powersync/attachments';
25
25
  export { AbstractPowerSyncDatabase as PowerSyncDBInterface } from '@powersync/common';
26
- export { g as DatabaseColumnDef, D as DatabaseSchemaObject, f as DatabaseTableDef, P as PolConfig, c as PolConfigAttachments, d as PolConfigConnector, e as PolConfigSync, a as ProcessedPolConfig, b as ProcessedTableStrategy, T as TableSpec } from './types-BhAEsJj-.js';
26
+ export { g as DatabaseColumnDef, D as DatabaseSchemaObject, f as DatabaseTableDef, P as PolConfig, c as PolConfigAttachments, d as PolConfigConnector, e as PolConfigSync, a as ProcessedPolConfig, b as ProcessedTableStrategy, T as TableSpec } from './types-DIp7AoIe.js';
27
27
  import '@supabase/supabase-js';
28
28
  import '@powersync/react-native';
29
29
  import 'react';
@@ -18,7 +18,7 @@ import {
18
18
  isProcessedPolConfig,
19
19
  isTablePowerSync,
20
20
  normalizeTableSpec
21
- } from "./chunk-HRAVPIAZ.js";
21
+ } from "./chunk-RLQOUNAG.js";
22
22
  import {
23
23
  DOWNLOAD_WORKFLOW_STATES,
24
24
  STATE_MAPPING,
@@ -22,7 +22,7 @@ export { AsyncStorageAdapter, CompressedImage, CompressionOptions, ConnectionTyp
22
22
  export { createWebPlatformAdapter } from './platform/index.web.js';
23
23
  export { ATTACHMENT_TABLE, AbstractAttachmentQueue, AttachmentState, AttachmentTable, AttachmentTableOptions, AttachmentQueueOptions as BaseAttachmentQueueOptions, DEFAULT_ATTACHMENT_QUEUE_OPTIONS, EncodingType, AttachmentRecord as OfficialAttachmentRecord, StorageAdapter } from '@powersync/attachments';
24
24
  export { AbstractPowerSyncDatabase as PowerSyncDBInterface } from '@powersync/common';
25
- export { g as DatabaseColumnDef, D as DatabaseSchemaObject, f as DatabaseTableDef, P as PolConfig, c as PolConfigAttachments, d as PolConfigConnector, e as PolConfigSync, a as ProcessedPolConfig, b as ProcessedTableStrategy, T as TableSpec } from './types-BhAEsJj-.js';
25
+ export { g as DatabaseColumnDef, D as DatabaseSchemaObject, f as DatabaseTableDef, P as PolConfig, c as PolConfigAttachments, d as PolConfigConnector, e as PolConfigSync, a as ProcessedPolConfig, b as ProcessedTableStrategy, T as TableSpec } from './types-DIp7AoIe.js';
26
26
  import '@supabase/supabase-js';
27
27
  import '@powersync/react-native';
28
28
  import 'react';
package/dist/index.web.js CHANGED
@@ -13,7 +13,7 @@ import {
13
13
  isProcessedPolConfig,
14
14
  isTablePowerSync,
15
15
  normalizeTableSpec
16
- } from "./chunk-HRAVPIAZ.js";
16
+ } from "./chunk-RLQOUNAG.js";
17
17
  import {
18
18
  DOWNLOAD_WORKFLOW_STATES,
19
19
  STATE_MAPPING,
@@ -11,7 +11,7 @@ import react__default, { ReactNode } from 'react';
11
11
  import { Schema } from '@powersync/react-native';
12
12
  import { a as StorageBackend } from '../types-CGMibJKD.js';
13
13
  import { h as BackgroundSyncSystem, c as SyncControlActions } from '../background-sync-BujnI3IR.js';
14
- import { a as ProcessedPolConfig } from '../types-BhAEsJj-.js';
14
+ import { a as ProcessedPolConfig } from '../types-DIp7AoIe.js';
15
15
  import '@powersync/common';
16
16
  import '@powersync/attachments';
17
17
 
@@ -14,7 +14,7 @@ import '../supabase-connector-HMxBA9Kg.js';
14
14
  import '@powersync/react-native';
15
15
  import '../types-CGMibJKD.js';
16
16
  import '../background-sync-BujnI3IR.js';
17
- import '../types-BhAEsJj-.js';
17
+ import '../types-DIp7AoIe.js';
18
18
 
19
19
  /**
20
20
  * OfflineDataProvider Component for @pol-studios/powersync
@@ -13,4 +13,4 @@ import 'react';
13
13
  import '@powersync/react-native';
14
14
  import '../types-CGMibJKD.js';
15
15
  import '../background-sync-BujnI3IR.js';
16
- import '../types-BhAEsJj-.js';
16
+ import '../types-DIp7AoIe.js';
@@ -256,7 +256,13 @@ interface ProcessedTableStrategy {
256
256
  schema: string;
257
257
  /** Table name */
258
258
  table: string;
259
- /** PowerSync SQLite alias (e.g., CoreUserGroup for core.UserGroup) */
259
+ /**
260
+ * PowerSync SQLite alias.
261
+ *
262
+ * Collision-aware aliasing:
263
+ * - Unique table names use just the table name (e.g., "Profile")
264
+ * - Colliding table names use SchemaTable format (e.g., "CoreProfile", "PublicProfile")
265
+ */
260
266
  alias: string;
261
267
  }
262
268
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pol-studios/powersync",
3
- "version": "1.0.34",
3
+ "version": "1.0.35",
4
4
  "description": "Enterprise PowerSync integration for offline-first applications",
5
5
  "license": "UNLICENSED",
6
6
  "type": "module",
@@ -155,7 +155,7 @@
155
155
  "commander": "^12.0.0",
156
156
  "jiti": "^2.4.0",
157
157
  "picocolors": "^1.0.0",
158
- "@pol-studios/db": "1.0.63"
158
+ "@pol-studios/db": "1.0.64"
159
159
  },
160
160
  "peerDependencies": {
161
161
  "@powersync/react-native": ">=1.0.8",
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/config/schema-generator.ts","../src/config/index.ts"],"sourcesContent":["/**\n * Schema Generator for @pol-studios/powersync\n *\n * Generates PowerSync Schema at runtime from the Supabase database schema object.\n * This eliminates the need to manually maintain a separate PowerSync schema file.\n *\n * Type Mapping:\n * - number -> column.real (ALL numbers use real, no integer/real distinction)\n * - boolean -> column.integer (SQLite stores booleans as 0/1)\n * - string -> column.text\n * - date -> column.text (ISO strings)\n * - enum -> column.text\n * - json -> SKIPPED (not supported in PowerSync)\n * - arrays -> SKIPPED (not supported in PowerSync)\n *\n * Skipped columns:\n * - `id` (PowerSync handles this automatically)\n * - JSON types (complex objects not supported)\n * - Array types (not supported)\n */\n\nimport { column, Schema, Table } from '@powersync/react-native';\nimport type { DatabaseSchemaObject, DatabaseTableDef, DatabaseColumnDef, TableSpec } from './types';\nimport { LOCAL_ONLY_TABLES } from '../sync/local-tables';\n\n// Type alias for PowerSync column types (same as what column.real/integer/text return)\ntype PowerSyncColumn = typeof column.real | typeof column.integer | typeof column.text;\n\n// ─── Table Spec Normalization ─────────────────────────────────────────────────\n\n/**\n * Normalized table specification.\n */\ninterface NormalizedTableSpec {\n /** Table name (without schema prefix) */\n tableName: string;\n /** Schema name */\n schemaName: string;\n /** Whether to track sync metadata */\n trackMetadata: boolean;\n /**\n * Alias for PowerSync table name.\n * For cross-schema tables, this is SchemaTable (e.g., \"CoreProfile\").\n * For public schema, this is just the table name.\n */\n alias: string;\n}\n\n/**\n * Normalize a TableSpec to a consistent format.\n *\n * @param spec - The table specification (string or object)\n * @returns Normalized specification with all fields filled\n */\nexport function normalizeTableSpec(spec: TableSpec): NormalizedTableSpec {\n if (typeof spec === 'string') {\n // Check for schema prefix (e.g., \"core.Profile\")\n if (spec.includes('.')) {\n const [schemaName, tableName] = spec.split('.');\n return {\n tableName,\n schemaName,\n trackMetadata: false,\n alias: toPascalCase(schemaName) + tableName\n };\n }\n // Simple table name - public schema\n return {\n tableName: spec,\n schemaName: 'public',\n trackMetadata: false,\n alias: spec\n };\n }\n\n // Object format\n const schemaName = spec.schema || 'public';\n const alias = schemaName === 'public' ? spec.table : toPascalCase(schemaName) + spec.table;\n return {\n tableName: spec.table,\n schemaName,\n trackMetadata: spec.trackMetadata ?? false,\n alias\n };\n}\n\n/**\n * Convert a string to PascalCase.\n */\nfunction toPascalCase(str: string): string {\n return str.charAt(0).toUpperCase() + str.slice(1);\n}\n\n// ─── Type Mapping ─────────────────────────────────────────────────────────────\n\n/**\n * Map a database column type to a PowerSync column type.\n *\n * @param col - The column definition\n * @returns PowerSync column type or null if should be skipped\n */\nfunction mapColumnType(col: DatabaseColumnDef): PowerSyncColumn | null {\n const type = col.type;\n\n // Skip JSON types - not supported in PowerSync\n if (type === 'json') {\n return null;\n }\n\n // Map based on type\n switch (type) {\n case 'number':\n // ALL numbers use column.real - no heuristics\n return column.real;\n case 'boolean':\n // SQLite stores booleans as integers (0/1)\n return column.integer;\n case 'string':\n case 'date':\n case 'enum':\n return column.text;\n default:\n // Unknown types default to text\n return column.text;\n }\n}\n\n/**\n * Check if a column should be skipped.\n *\n * @param col - The column definition\n * @returns true if the column should be skipped\n */\nfunction shouldSkipColumn(col: DatabaseColumnDef): boolean {\n // Skip `id` - PowerSync handles this automatically\n if (col.name === 'id') {\n return true;\n }\n\n // Skip JSON types\n if (col.type === 'json') {\n return true;\n }\n\n // Skip array-like columns (columns ending with 's' that are JSON)\n // This is a heuristic - actual array detection would need type info\n // For now, we rely on the type being 'json' which is already skipped\n\n return false;\n}\n\n// ─── Table Lookup ─────────────────────────────────────────────────────────────\n\n/**\n * Get a table definition from the database schema.\n *\n * @param databaseSchema - The full database schema object\n * @param schemaName - The schema name (e.g., \"public\", \"core\")\n * @param tableName - The table name\n * @returns The table definition or null if not found\n */\nexport function getTableFromSchema(databaseSchema: DatabaseSchemaObject, schemaName: string, tableName: string): DatabaseTableDef | null {\n const schema = databaseSchema.schemas[schemaName];\n if (!schema) {\n console.warn(`[PolConfig] Schema \"${schemaName}\" not found in databaseSchema`);\n return null;\n }\n const table = schema.tables[tableName];\n if (!table) {\n // Also check views\n const view = schema.views?.[tableName];\n if (view) {\n return view;\n }\n console.warn(`[PolConfig] Table \"${schemaName}.${tableName}\" not found in databaseSchema`);\n return null;\n }\n return table;\n}\n\n// ─── Schema Generation ────────────────────────────────────────────────────────\n\n/**\n * Result of schema generation.\n */\nexport interface SchemaGenerationResult {\n /** The generated PowerSync Schema */\n schema: Schema;\n /** Warnings encountered during generation */\n warnings: string[];\n /** Map of table alias to normalized spec */\n tableMap: Map<string, NormalizedTableSpec>;\n}\n\n/**\n * Generate a PowerSync Schema from the database schema and table specs.\n *\n * @param databaseSchema - The database schema object (from generated types)\n * @param tables - Array of table specifications to sync\n * @returns The generated PowerSync Schema and metadata\n *\n * @example\n * ```typescript\n * const { schema, warnings } = generatePowerSyncSchema(databaseSchema, [\n * \"Project\",\n * \"Task\",\n * { table: \"Profile\", schema: \"core\" },\n * ]);\n *\n * // Use the schema with PowerSyncProvider\n * <PowerSyncProvider config={{ schema, ... }} />\n * ```\n */\nexport function generatePowerSyncSchema(databaseSchema: DatabaseSchemaObject, tables: TableSpec[]): SchemaGenerationResult {\n const warnings: string[] = [];\n const tableDefinitions: Record<string, Table> = {};\n const tableMap = new Map<string, NormalizedTableSpec>();\n for (const spec of tables) {\n const normalized = normalizeTableSpec(spec);\n const {\n tableName,\n schemaName,\n alias\n } = normalized;\n\n // Get table definition from schema\n const tableDef = getTableFromSchema(databaseSchema, schemaName, tableName);\n if (!tableDef) {\n warnings.push(`Table \"${schemaName}.${tableName}\" not found, skipping`);\n continue;\n }\n\n // Build PowerSync columns\n const columns: Record<string, PowerSyncColumn> = {};\n let columnCount = 0;\n for (const col of tableDef.columns) {\n // Skip certain columns\n if (shouldSkipColumn(col)) {\n continue;\n }\n\n // Map the column type\n const psType = mapColumnType(col);\n if (psType === null) {\n // Column type not supported, skip with warning\n warnings.push(`Column \"${tableName}.${col.name}\" has unsupported type \"${col.type}\", skipping`);\n continue;\n }\n columns[col.name] = psType;\n columnCount++;\n }\n\n // Create PowerSync Table\n if (columnCount > 0) {\n // Check for duplicate tables\n if (tableDefinitions[alias]) {\n warnings.push(`Duplicate table \"${alias}\" - later definition wins`);\n }\n\n // Pass trackMetadata option if specified\n tableDefinitions[alias] = normalized.trackMetadata ? new Table(columns, {\n trackMetadata: true\n }) : new Table(columns);\n tableMap.set(alias, normalized);\n } else {\n warnings.push(`Table \"${schemaName}.${tableName}\" has no valid columns after filtering, skipping`);\n }\n }\n\n // Add local-only tables for sync status persistence\n const allTableDefinitions = {\n ...tableDefinitions,\n ...LOCAL_ONLY_TABLES\n };\n\n // Create the Schema\n const schema = new Schema(allTableDefinitions);\n return {\n schema,\n warnings,\n tableMap\n };\n}\n\n// ─── Schema Router ────────────────────────────────────────────────────────────\n\n/**\n * Create a schema router function from the table map.\n *\n * @param tableMap - Map of table alias to normalized spec\n * @returns A function that returns the schema for a given table\n */\nexport function createSchemaRouter(tableMap: Map<string, NormalizedTableSpec>): (table: string) => string {\n return (table: string): string => {\n // Check if it's a known PowerSync table\n const spec = tableMap.get(table);\n if (spec) {\n return spec.schemaName;\n }\n\n // Check for schema-prefixed format (e.g., \"core.Profile\")\n if (table.includes('.')) {\n const [schema] = table.split('.');\n return schema;\n }\n\n // Default to public\n return 'public';\n };\n}","/**\n * Unified Configuration for @pol-studios/powersync\n *\n * This module provides the `definePolConfig` function for creating\n * a unified configuration that automatically generates:\n * - PowerSync Schema from the database schema\n * - Table routing strategies for DataLayer\n * - Attachment queue configuration\n *\n * @example\n * ```typescript\n * import { definePolConfig } from '@pol-studios/powersync/config';\n * import { databaseSchema } from './databaseSchema';\n *\n * export const polConfig = definePolConfig({\n * powerSyncUrl: process.env.EXPO_PUBLIC_POWERSYNC_URL,\n * databaseSchema,\n * powersync: [\"Project\", \"Task\", { table: \"Profile\", schema: \"core\" }],\n * attachments: {\n * source: { type: 'supabase-bucket', bucket: 'photos' },\n * watchPaths: (db, supabase, onUpdate) => {\n * const abort = new AbortController();\n * db.watch(\n * `SELECT storagePath FROM photos WHERE storagePath IS NOT NULL`,\n * [],\n * { onResult: (r) => onUpdate(r.rows._array.map(x => x.storagePath)) },\n * { signal: abort.signal }\n * );\n * return () => abort.abort();\n * },\n * },\n * });\n *\n * // Use with OfflineDataProvider\n * <OfflineDataProvider polConfig={polConfig}>\n * <App />\n * </OfflineDataProvider>\n * ```\n */\n\nimport type { PolConfig, ProcessedPolConfig, ProcessedTableStrategy, TableSpec } from './types';\nimport { generatePowerSyncSchema, normalizeTableSpec, createSchemaRouter } from './schema-generator';\n\n// Re-export types\nexport type { PolConfig, ProcessedPolConfig, TableSpec, ProcessedTableStrategy, PolConfigAttachments, PolConfigConnector, PolConfigSync, DatabaseSchemaObject, DatabaseTableDef, DatabaseColumnDef,\n// Alias for attachment source type from config perspective\nPolAttachmentSource } from './types';\n\n// Re-export attachment source types from attachments module\n// (these are the canonical definitions)\nexport type { AttachmentSource, SupabaseBucketSource, CustomAttachmentSource } from '../attachments/types';\n\n// Re-export schema generator utilities\nexport { generatePowerSyncSchema, normalizeTableSpec, createSchemaRouter, getTableFromSchema } from './schema-generator';\n\n// ─── Configuration Processing ─────────────────────────────────────────────────\n\n/**\n * Create a unified PowerSync configuration.\n *\n * This function processes the configuration to generate:\n * - `__generatedSchema`: PowerSync Schema ready for use\n * - `__tableStrategies`: Lookup map for routing queries\n * - `__schemaRouter`: Function to resolve table schemas\n *\n * @param config - The unified configuration\n * @returns Processed configuration with generated artifacts\n *\n * @example\n * ```typescript\n * const polConfig = definePolConfig({\n * powerSyncUrl: 'https://your-instance.powersync.journeyapps.com',\n * databaseSchema,\n * powersync: [\n * \"Project\",\n * \"Task\",\n * { table: \"Profile\", schema: \"core\" },\n * ],\n * });\n *\n * // Access generated schema\n * console.log(polConfig.__generatedSchema);\n *\n * // Check if a table uses PowerSync\n * const strategy = polConfig.__tableStrategies[\"Project\"];\n * console.log(strategy.strategy); // \"powersync\"\n * ```\n */\nexport function definePolConfig(config: PolConfig): ProcessedPolConfig {\n // Validate required fields\n if (!config.databaseSchema) {\n throw new Error('[definePolConfig] databaseSchema is required');\n }\n if (!config.powersync || config.powersync.length === 0) {\n throw new Error('[definePolConfig] powersync array must contain at least one table');\n }\n\n // Generate PowerSync schema\n const {\n schema,\n warnings,\n tableMap\n } = generatePowerSyncSchema(config.databaseSchema, config.powersync);\n\n // Log warnings in development (safe check for non-React-Native environments)\n if (warnings.length > 0 && typeof __DEV__ !== 'undefined' && __DEV__) {\n console.warn('[definePolConfig] Schema generation warnings:', warnings);\n }\n\n // Validate that at least one table was successfully generated\n if (tableMap.size === 0) {\n throw new Error('[definePolConfig] No valid tables found in powersync array. ' + `Check that the tables exist in databaseSchema. Warnings: ${warnings.join(', ')}`);\n }\n\n // Build table strategies lookup\n const tableStrategies: Record<string, ProcessedTableStrategy> = {};\n const powersyncTables: string[] = [];\n for (const [alias, spec] of tableMap.entries()) {\n // Add to strategies map with alias as key\n tableStrategies[alias] = {\n strategy: 'powersync',\n schema: spec.schemaName,\n table: spec.tableName,\n alias: alias // PowerSync SQLite table name\n };\n\n // Also add with full qualified name for lookup\n const qualifiedName = spec.schemaName === 'public' ? spec.tableName : `${spec.schemaName}.${spec.tableName}`;\n if (qualifiedName !== alias) {\n tableStrategies[qualifiedName] = {\n strategy: 'powersync',\n schema: spec.schemaName,\n table: spec.tableName,\n alias: alias // PowerSync SQLite table name (same alias for both keys)\n };\n }\n powersyncTables.push(qualifiedName);\n }\n\n // Create schema router\n const schemaRouter = createSchemaRouter(tableMap);\n\n // Return processed config\n const processedConfig: ProcessedPolConfig = {\n ...config,\n __generatedSchema: schema,\n __tableStrategies: tableStrategies,\n __schemaRouter: schemaRouter,\n __powersyncTables: powersyncTables\n };\n return processedConfig;\n}\n\n// ─── Helper Functions ─────────────────────────────────────────────────────────\n\n/**\n * Check if a configuration is a processed PolConfig.\n *\n * @param config - The configuration to check\n * @returns true if the config has been processed by definePolConfig\n */\nexport function isProcessedPolConfig(config: unknown): config is ProcessedPolConfig {\n return typeof config === 'object' && config !== null && '__generatedSchema' in config && '__tableStrategies' in config;\n}\n\n/**\n * Get the strategy for a table from a processed config.\n *\n * @param config - The processed configuration\n * @param tableName - Table name (can be \"Table\" or \"schema.Table\")\n * @returns The strategy or undefined if not found\n */\nexport function getTableStrategy(config: ProcessedPolConfig, tableName: string): ProcessedTableStrategy | undefined {\n return config.__tableStrategies[tableName];\n}\n\n/**\n * Check if a table uses PowerSync for sync.\n *\n * @param config - The processed configuration\n * @param tableName - Table name\n * @returns true if the table is synced via PowerSync\n */\nexport function isTablePowerSync(config: ProcessedPolConfig, tableName: string): boolean {\n const strategy = getTableStrategy(config, tableName);\n return strategy?.strategy === 'powersync';\n}\n\n// ─── Global Declaration ───────────────────────────────────────────────────────\n\n// Declare __DEV__ global for React Native\ndeclare const __DEV__: boolean;"],"mappings":"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","names":["schemaName"]}