このコミットが含まれているのは:
2026-07-16 21:45:02 +09:00
コミット 66c738cbff
26個のファイルの変更、1083行の追加、951行の削除
+1 -2
ファイルの表示
@@ -270,7 +270,6 @@ describe ('post import row state', () => {
thumbnailBase: '',
originalCreatedFrom: '',
originalCreatedBefore: '',
duration: '2.5',
tags: 'edited-tag',
parentPostIds: '' },
true)
@@ -279,7 +278,7 @@ describe ('post import row state', () => {
expect (nextRow.importErrors).toBeUndefined ()
expect (nextRow.url).toBe ('https://example.com/edited')
expect (nextRow.attributes.title).toBe ('edited title')
expect (nextRow.attributes.duration).toBe ('2.5')
expect (nextRow.attributes.duration).toBe ('2')
expect (nextRow.attributes.tags).toBe ('edited-tag')
expect (nextRow.provenance.title).toBe ('manual')
expect (nextRow.provenance.url).toBe ('manual')
-1
ファイルの表示
@@ -128,7 +128,6 @@ export const buildNextEditedRow = (
['thumbnailBase', draft.thumbnailBase],
['originalCreatedFrom', draft.originalCreatedFrom],
['originalCreatedBefore', draft.originalCreatedBefore],
['duration', draft.duration],
['parentPostIds', draft.parentPostIds]] as const
draftFields.forEach (([field, value]) => {
nextAttributes[field] = value
+11 -9
ファイルの表示
@@ -25,11 +25,11 @@ describe ('post import storage', () => {
skipReason: 'existing',
existingPostId: 10 })
expect (savePostImportSession ('session', {
expect (savePostImportSession ({
source: skipped.url,
rows: [row, skipped],
repairMode: 'all' })).toBe (true)
expect (loadPostImportSession ('session')).toMatchObject ({
expect (loadPostImportSession ()).toMatchObject ({
version: 2,
source: skipped.url,
rows: [{
@@ -63,12 +63,13 @@ describe ('post import storage', () => {
{ ...session.rows[0], importStatus: 'skipped', recoverable: true },
{ ...session.rows[0], recoverable: true }]
for (const [index, row] of invalidRows.entries ())
for (const row of invalidRows)
{
sessionStorage.setItem (`post-import-session:invalid-${ index }`, JSON.stringify ({
sessionStorage.setItem ('post-import-session:current', JSON.stringify ({
...session,
rows: [row] }))
expect (loadPostImportSession (`invalid-${ index }`)).toBeNull ()
expect (loadPostImportSession ()).toBeNull ()
sessionStorage.clear ()
}
})
@@ -82,18 +83,19 @@ describe ('post import storage', () => {
{ ...row, resetSnapshot: { ...row.resetSnapshot,
fieldWarnings: { title: 'warning' } } }]
invalidRows.forEach ((invalidRow, index) => {
sessionStorage.setItem (`post-import-session:shape-${ index }`, JSON.stringify ({
invalidRows.forEach (invalidRow => {
sessionStorage.setItem ('post-import-session:current', JSON.stringify ({
version: 2,
savedAt: new Date ().toISOString (),
source: '',
rows: [invalidRow],
repairMode: 'all' }))
expect (loadPostImportSession (`shape-${ index }`)).toBeNull ()
expect (loadPostImportSession ()).toBeNull ()
sessionStorage.clear ()
})
})
it ('removes expired and malformed sessions without touching current sessions', () => {
it ('keeps only the current fixed-key session and removes legacy prefixed entries', () => {
const current = {
version: 2,
savedAt: new Date ().toISOString (),
+39 -16
ファイルの表示
@@ -8,6 +8,7 @@ import type { PostImportOrigin,
const SESSION_VERSION = 2
const SESSION_PREFIX = 'post-import-session:'
const CURRENT_SESSION_KEY = `${ SESSION_PREFIX }current`
const SOURCE_DRAFT_KEY = 'post-import-source-draft'
const SESSION_MAX_AGE_MS = 24 * 60 * 60 * 1000
const ATTRIBUTE_KEYS = [
@@ -28,7 +29,7 @@ const isPlainObject = (value: unknown): value is Record<string, unknown> =>
typeof value === 'object' && value != null && !(Array.isArray (value))
const sessionKey = (sessionId: string): string => `${ SESSION_PREFIX }${ sessionId }`
const sessionKey = (): string => CURRENT_SESSION_KEY
const readStorage = (
@@ -288,12 +289,6 @@ const isExpiredSession = (savedAt: string): boolean => {
}
export const createPostImportSessionId = (): string =>
typeof crypto !== 'undefined' && 'randomUUID' in crypto
? crypto.randomUUID ()
: `${ Date.now () }-${ Math.random ().toString (36).slice (2) }`
export const cleanupExpiredPostImportSessions = (
onError?: StorageErrorHandler,
) => {
@@ -311,6 +306,13 @@ export const cleanupExpiredPostImportSessions = (
const raw = sessionStorage.getItem (key)
if (raw == null)
continue
if (key !== CURRENT_SESSION_KEY)
{
sessionStorage.removeItem (key)
--i
continue
}
try
{
const value = JSON.parse (raw) as { savedAt?: string }
@@ -368,24 +370,38 @@ export const clearPostImportSourceDraft = (
export const savePostImportSession = (
sessionId: string,
session: Omit<PostImportSession, 'version' | 'savedAt'>,
sessionOrLegacyId: string | Omit<PostImportSession, 'version' | 'savedAt'>,
sessionOrOnError?: Omit<PostImportSession, 'version' | 'savedAt'> | StorageErrorHandler,
onError?: StorageErrorHandler,
): boolean =>
writeStorage (
sessionKey (sessionId),
): boolean => {
const session =
typeof sessionOrLegacyId === 'string'
? sessionOrOnError as Omit<PostImportSession, 'version' | 'savedAt'>
: sessionOrLegacyId
const errorHandler =
typeof sessionOrLegacyId === 'string'
? onError
: sessionOrOnError as StorageErrorHandler | undefined
return writeStorage (
sessionKey (),
JSON.stringify ({
...session,
version: SESSION_VERSION,
savedAt: new Date ().toISOString () }),
onError)
errorHandler)
}
export const loadPostImportSession = (
sessionId: string,
legacyIdOrOnError?: string | StorageErrorHandler,
onError?: StorageErrorHandler,
): PostImportSession | null => {
const raw = readStorage (sessionKey (sessionId), onError)
const errorHandler =
typeof legacyIdOrOnError === 'string'
? onError
: legacyIdOrOnError
const raw = readStorage (sessionKey (), errorHandler)
if (raw == null)
return null
@@ -396,7 +412,7 @@ export const loadPostImportSession = (
return null
if (typeof value.savedAt !== 'string' || isExpiredSession (value.savedAt))
{
removeStorage (sessionKey (sessionId), onError)
removeStorage (sessionKey (), errorHandler)
return null
}
@@ -416,3 +432,10 @@ export const loadPostImportSession = (
return null
}
}
export const clearPostImportSession = (
onError?: StorageErrorHandler,
) => {
removeStorage (sessionKey (), onError)
}
-1
ファイルの表示
@@ -78,7 +78,6 @@ export type PostImportEditableDraft = {
thumbnailBase: string
originalCreatedFrom: string
originalCreatedBefore: string
duration: string
tags: string
parentPostIds: string }