@@ -17,8 +17,9 @@ import {
1717import { generateCloneId } from '../utilities/analytics/utils/idGenerator' ;
1818import { SnapValidator } from '../validation/snapValidator' ;
1919import { ValidationResult } from '../validation/validationTypes' ;
20- import { ProcessorInput , getFs , getNodeRequire , getPath , isNodeRuntime } from '../utils/io' ;
20+ import { ProcessorInput , getFs , getNodeRequire , getPath , isNodeRuntime , getOs } from '../utils/io' ;
2121import { openSqliteDatabase , requireBetterSqlite3 } from '../utils/sqlite' ;
22+ import { openZipFromInput } from '../utils/zip' ;
2223
2324/**
2425 * Convert a Buffer or Uint8Array to base64 string (browser and Node compatible)
@@ -123,8 +124,45 @@ class SnapProcessor extends BaseProcessor {
123124 await Promise . resolve ( ) ;
124125 const tree = new AACTree ( ) ;
125126 let dbResult : Awaited < ReturnType < typeof openSqliteDatabase > > | null = null ;
127+ let cleanupTempZip : ( ( ) => void ) | null = null ;
128+
126129 try {
127- dbResult = await openSqliteDatabase ( filePathOrBuffer , { readonly : true } ) ;
130+ // Handle .sub.zip files (Snap pageset backups containing .sps files)
131+ let inputFile = filePathOrBuffer ;
132+
133+ if ( typeof filePathOrBuffer === 'string' ) {
134+ const fileName = getPath ( ) . basename ( filePathOrBuffer ) . toLowerCase ( ) ;
135+ if ( fileName . endsWith ( '.sub.zip' ) || filePathOrBuffer . endsWith ( '.sub' ) ) {
136+ const fs = getFs ( ) ;
137+ const path = getPath ( ) ;
138+ const os = getOs ( ) ;
139+
140+ // Extract .sub.zip to find the embedded .sps file
141+ const tempDir = fs . mkdtempSync ( path . join ( os . tmpdir ( ) , 'snap-sub-' ) ) ;
142+ const { zip } = await openZipFromInput ( filePathOrBuffer ) ;
143+
144+ // Find the .sps file in the archive
145+ const files = zip . listFiles ( ) ;
146+ const spsFile = files . find ( ( f ) => f . endsWith ( '.sps' ) ) ;
147+
148+ if ( ! spsFile ) {
149+ fs . rmSync ( tempDir , { recursive : true , force : true } ) ;
150+ throw new Error ( 'No .sps file found in .sub.zip archive' ) ;
151+ }
152+
153+ // Extract the .sps file
154+ const spsData = await zip . readFile ( spsFile ) ;
155+ const extractedSpsPath = path . join ( tempDir , path . basename ( spsFile ) ) ;
156+ fs . writeFileSync ( extractedSpsPath , Buffer . from ( spsData ) ) ;
157+
158+ inputFile = extractedSpsPath ;
159+ cleanupTempZip = ( ) => {
160+ fs . rmSync ( tempDir , { recursive : true , force : true } ) ;
161+ } ;
162+ }
163+ }
164+
165+ dbResult = await openSqliteDatabase ( inputFile , { readonly : true } ) ;
128166 const db = dbResult . db ;
129167
130168 const getTableColumns = ( tableName : string ) : Set < string > => {
@@ -789,6 +827,14 @@ class SnapProcessor extends BaseProcessor {
789827 } else if ( dbResult ?. db ) {
790828 dbResult . db . close ( ) ;
791829 }
830+ // Clean up temporary extracted .sps file from .sub.zip
831+ if ( cleanupTempZip ) {
832+ try {
833+ cleanupTempZip ( ) ;
834+ } catch ( e ) {
835+ console . warn ( '[SnapProcessor] Failed to clean up temporary .sps file:' , e ) ;
836+ }
837+ }
792838 }
793839 }
794840
0 commit comments