You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

331 lines
9.0 KiB

import {
AUTH_CACHE_TAGS,
AUTH_COOKIE_NAMES,
AUTH_DEFAULTS,
AUTH_EVENT_NAMES,
AUTH_ROUTE_PATHS,
AUTH_TEST_ACTIONS,
AUTH_TEST_COOKIE_NAMES,
AUTH_TEST_FORM_FIELDS,
AUTH_TEST_IDS,
AUTH_TEST_TENANT_ID,
issueAuthCsrf,
verifyAuthCsrf,
type AuthSessionId
} from '$libs/auth';
import {
createDeterministicAuthCrypto,
createEngineAuth,
createMemoryAuthActors,
createMemoryAuthAdapter,
Rename modules from 4-letter aliases to full English words Drops the 4-letter alias convention in favour of a single homogeneous naming axis: full English words across filesystem, alias, wire format and constants. Module renames: - arts/aapp → arts/active-app (libs/aapp also) - arts/buss → arts/bus (libs/buss also) - arts/cach → arts/cache (libs/cach + svrs/cach also) - arts/conn → arts/connection - arts/fend → arts/frontend - arts/fmts → arts/formats (curr→currency, nums→numbers, unts→units) - arts/logr → arts/logger (libs/logr also) - arts/perm → arts/permissions (libs/perm + svrs/perm also) - arts/sess → arts/session - arts/stor → arts/storage - arts/timr → arts/timer (libs/timers → libs/timer) Modules left as-is: auth, dom, errs, http, lang, sium (already match their canonical name or are proper names). Special case: `aapp` could not become `app` because `$app` is reserved by SvelteKit (`$app/stores`, `$app/navigation`, ...). Compromise: - Filesystem and alias use `active-app` / `$active-app`. - Constants and class names use `App` / `APP_*` (no `active-` prefix). The `active-` prefix only disambiguates the alias from SvelteKit's namespace; the module is App. Special case: `permissions` keeps the plural for filesystem/alias/wire but constants and classes use the singular `PERMISSION_*` / `Permission*` because they describe the concept ("a permission effect"), not the module collection. Constants follow the new module name in caps: `STORAGE_*`, `BUS_*`, `CACHE_*`, `CONNECTION_*`, `FORMATS_*`, `LOGGER_*`, `SESSION_*`, `TIMER_*`, etc. Module values: `STORAGE_MODULE = 'storage'`, `BUS_MODULE = 'bus'`, `APP_MODULE = 'app'`, `PERMISSION_MODULE = 'permissions'`, etc. Wire/code format moved accordingly: `'storage::*'`, `'bus::*'`, `'session::*'`, `'permissions::*'`, etc. Diagnostic event values updated: `'storage.error'`, `'bus.event.published'`, `'connection.auth_failed'`, etc. App events use `'app.*'`: `AAPP_EVENT_* → APP_EVENT_*` with values `'app.user.identity.changed'`. Class renames (where they used the abbreviation): - AappAlreadyCreatedError → AppAlreadyCreatedError - BussError* → BusError* (where applicable) - Cach* → Cache* - Conn* → Connection* (e.g. ConnDisposedError → ConnectionDisposedError; ConnConnection* collapsed to Connection*) - Logr*Error → Logger*Error - Sess* → Session* (SessInvalidSessionError → SessionInvalidError) - Stor* → Storage* - Timr* → Timer* (TimrInactiveTimerError → TimerInactiveError) - AuthCachPort → AuthCachePort - AuthClientCach* → AuthClientCache* - AuthPermPort → AuthPermissionsPort Property renames in option types: - `cach?:` → `cache?:` in AuthClient options - `logr:` → `logger:` in svrs/auth ports - `timr:` → `timer:` in svrs/auth ports `docs/conventions.md` rewritten: - Rule 1 dropped the 4-letter alias mandate; lists the full English module names and special-cases active-app, lang, sium, permissions. - Rule 2 documents the new constant prefix convention and its two exceptions (APP_* for active-app, PERMISSION_* singular for permissions). - Rule 6 codifies that all error infrastructure (codes, messages, classes, guards) lives in a single `errors.ts` per module — removing the `consts.ts` / `errors.ts` split for error-related symbols. `libs/errs` adds `ErrorMessages` type so every module can declare its catalog as `<MOD>_ERROR_MESSAGES: ErrorMessages` instead of repeating the `Readonly<Record<ErrCode, string | (...args) => string>>` shape. Storage migrated as the first proof of the canonical pattern. All 1334 tests pass. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
createMemoryAuthCache,
createMemoryAuthClock,
createMemoryAuthLogr,
createMemoryAuthMailer,
createMemoryAuthSessPort,
createTestPasswordHasher
} from '$svrs/auth';
export const prerender = false;
const AUTH_TEST_PASSWORD = 'correct horse battery staple';
const AUTH_TEST_CSRF_KEY = 'test-page-csrf-key';
const AUTH_TEST_SESSION_COOKIE_MAX_AGE = 60 * 60;
let harness = createHarness();
export async function load({ cookies }: { cookies: { get(name: string): string | undefined } }) {
const sessionId = cookies.get(AUTH_TEST_COOKIE_NAMES.SESSION) as AuthSessionId | undefined;
return {
auth: {
routes: AUTH_ROUTE_PATHS,
events: AUTH_EVENT_NAMES,
cacheTags: AUTH_CACHE_TAGS,
testIds: AUTH_TEST_IDS,
actions: AUTH_TEST_ACTIONS,
fields: AUTH_TEST_FORM_FIELDS,
defaults: {
identifier: 'ada@example.com',
password: AUTH_TEST_PASSWORD,
displayName: 'Ada Lovelace'
}
},
state: await snapshot(sessionId)
};
}
export const actions = {
async signUp({ request, cookies }: TestActionEvent) {
return runAuthAction(cookies, AUTH_TEST_ACTIONS.SIGN_UP, async () => {
const input = await readPasswordForm(request);
const result = await harness.engine.signUpPassword({
tenantId: AUTH_TEST_TENANT_ID,
identifier: input.identifier,
password: input.password,
profile: { displayName: input.displayName },
device: {
displayName: 'Test browser',
userAgentDisplay: 'SvelteKit action'
}
});
writeSessionCookie(cookies, result.session.sessionId);
return { current: result.current };
});
},
async signIn({ request, cookies }: TestActionEvent) {
return runAuthAction(cookies, AUTH_TEST_ACTIONS.SIGN_IN, async () => {
const input = await readPasswordForm(request);
const result = await harness.engine.signInPassword({
tenantId: AUTH_TEST_TENANT_ID,
identifier: input.identifier,
password: input.password,
device: {
displayName: 'Test browser',
userAgentDisplay: 'SvelteKit action'
}
});
writeSessionCookie(cookies, result.session.sessionId);
return { current: result.current };
});
},
async signOut({ cookies }: TestActionEvent) {
return runAuthAction(cookies, AUTH_TEST_ACTIONS.SIGN_OUT, async () => {
const sessionId = readSessionCookie(cookies);
const result = await harness.engine.signOut({
tenantId: AUTH_TEST_TENANT_ID,
sessionId
});
clearSessionCookie(cookies);
return { current: result.current, endedSessionIds: result.endedSessionIds };
});
},
async signOutGlobal({ cookies }: TestActionEvent) {
return runAuthAction(cookies, AUTH_TEST_ACTIONS.SIGN_OUT_GLOBAL, async () => {
const sessionId = readSessionCookie(cookies);
const result = await harness.engine.signOutGlobal({
tenantId: AUTH_TEST_TENANT_ID,
sessionId
});
clearSessionCookie(cookies);
return { current: result.current, endedSessionIds: result.endedSessionIds };
});
},
async csrfRoundtrip({ cookies }: TestActionEvent) {
return runAuthAction(cookies, AUTH_TEST_ACTIONS.CSRF_ROUNDTRIP, async () => {
const result = await harness.engine.issueCsrf({ tenantId: AUTH_TEST_TENANT_ID });
await harness.engine.verifyCsrf({
tenantId: AUTH_TEST_TENANT_ID,
token: result.token,
cookie: result.cookie.value
});
return {
csrf: {
ok: true,
expiresAt: result.expiresAt,
cookieName: AUTH_COOKIE_NAMES.CSRF
}
};
});
},
async csrfExpired({ cookies }: TestActionEvent) {
return runAuthAction(cookies, AUTH_TEST_ACTIONS.CSRF_EXPIRED, async () => {
const crypto = createDeterministicAuthCrypto('auth-test-page-expired-csrf');
const clock = createMemoryAuthClock(10_000);
const result = await issueAuthCsrf({
crypto,
clock,
tenantId: AUTH_TEST_TENANT_ID,
config: { signingKey: AUTH_TEST_CSRF_KEY }
});
clock.advance(AUTH_DEFAULTS.CSRF_TTL_MS + 1);
try {
await verifyAuthCsrf({
crypto,
clock,
tenantId: AUTH_TEST_TENANT_ID,
token: result.token,
cookie: result.cookie.value,
config: { signingKey: AUTH_TEST_CSRF_KEY }
});
return { csrf: { ok: false, reason: 'unexpected-valid-token' } };
} catch (error) {
return { csrf: { ok: true, rejected: errorName(error) } };
}
});
},
async reset({ cookies }: TestActionEvent) {
harness = createHarness();
clearSessionCookie(cookies);
return {
ok: true,
action: AUTH_TEST_ACTIONS.RESET,
message: 'Auth test harness reset.',
state: await snapshot()
};
}
};
interface TestCookies {
get(name: string): string | undefined;
set(name: string, value: string, options: TestCookieOptions): void;
delete(name: string, options: Pick<TestCookieOptions, 'path'>): void;
}
interface TestCookieOptions {
readonly path: string;
readonly httpOnly?: boolean;
readonly sameSite?: 'strict' | 'lax' | 'none';
readonly secure?: boolean;
readonly maxAge?: number;
}
interface TestActionEvent {
readonly request: Request;
readonly cookies: TestCookies;
}
interface PasswordFormInput {
readonly identifier: string;
readonly password: string;
readonly displayName: string;
}
function createHarness() {
const crypto = createDeterministicAuthCrypto('auth-test-page');
const clock = createMemoryAuthClock(Date.now());
const store = createMemoryAuthAdapter({ suppressProductionWarning: true });
const actors = createMemoryAuthActors(crypto);
const sess = createMemoryAuthSessPort({ crypto, clock });
const logr = createMemoryAuthLogr();
Rename modules from 4-letter aliases to full English words Drops the 4-letter alias convention in favour of a single homogeneous naming axis: full English words across filesystem, alias, wire format and constants. Module renames: - arts/aapp → arts/active-app (libs/aapp also) - arts/buss → arts/bus (libs/buss also) - arts/cach → arts/cache (libs/cach + svrs/cach also) - arts/conn → arts/connection - arts/fend → arts/frontend - arts/fmts → arts/formats (curr→currency, nums→numbers, unts→units) - arts/logr → arts/logger (libs/logr also) - arts/perm → arts/permissions (libs/perm + svrs/perm also) - arts/sess → arts/session - arts/stor → arts/storage - arts/timr → arts/timer (libs/timers → libs/timer) Modules left as-is: auth, dom, errs, http, lang, sium (already match their canonical name or are proper names). Special case: `aapp` could not become `app` because `$app` is reserved by SvelteKit (`$app/stores`, `$app/navigation`, ...). Compromise: - Filesystem and alias use `active-app` / `$active-app`. - Constants and class names use `App` / `APP_*` (no `active-` prefix). The `active-` prefix only disambiguates the alias from SvelteKit's namespace; the module is App. Special case: `permissions` keeps the plural for filesystem/alias/wire but constants and classes use the singular `PERMISSION_*` / `Permission*` because they describe the concept ("a permission effect"), not the module collection. Constants follow the new module name in caps: `STORAGE_*`, `BUS_*`, `CACHE_*`, `CONNECTION_*`, `FORMATS_*`, `LOGGER_*`, `SESSION_*`, `TIMER_*`, etc. Module values: `STORAGE_MODULE = 'storage'`, `BUS_MODULE = 'bus'`, `APP_MODULE = 'app'`, `PERMISSION_MODULE = 'permissions'`, etc. Wire/code format moved accordingly: `'storage::*'`, `'bus::*'`, `'session::*'`, `'permissions::*'`, etc. Diagnostic event values updated: `'storage.error'`, `'bus.event.published'`, `'connection.auth_failed'`, etc. App events use `'app.*'`: `AAPP_EVENT_* → APP_EVENT_*` with values `'app.user.identity.changed'`. Class renames (where they used the abbreviation): - AappAlreadyCreatedError → AppAlreadyCreatedError - BussError* → BusError* (where applicable) - Cach* → Cache* - Conn* → Connection* (e.g. ConnDisposedError → ConnectionDisposedError; ConnConnection* collapsed to Connection*) - Logr*Error → Logger*Error - Sess* → Session* (SessInvalidSessionError → SessionInvalidError) - Stor* → Storage* - Timr* → Timer* (TimrInactiveTimerError → TimerInactiveError) - AuthCachPort → AuthCachePort - AuthClientCach* → AuthClientCache* - AuthPermPort → AuthPermissionsPort Property renames in option types: - `cach?:` → `cache?:` in AuthClient options - `logr:` → `logger:` in svrs/auth ports - `timr:` → `timer:` in svrs/auth ports `docs/conventions.md` rewritten: - Rule 1 dropped the 4-letter alias mandate; lists the full English module names and special-cases active-app, lang, sium, permissions. - Rule 2 documents the new constant prefix convention and its two exceptions (APP_* for active-app, PERMISSION_* singular for permissions). - Rule 6 codifies that all error infrastructure (codes, messages, classes, guards) lives in a single `errors.ts` per module — removing the `consts.ts` / `errors.ts` split for error-related symbols. `libs/errs` adds `ErrorMessages` type so every module can declare its catalog as `<MOD>_ERROR_MESSAGES: ErrorMessages` instead of repeating the `Readonly<Record<ErrCode, string | (...args) => string>>` shape. Storage migrated as the first proof of the canonical pattern. All 1334 tests pass. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
const cach = createMemoryAuthCache();
const mailer = createMemoryAuthMailer();
const engine = createEngineAuth({
security: { csrf: { signingKey: AUTH_TEST_CSRF_KEY } },
ports: {
store,
actors,
sess,
Rename modules from 4-letter aliases to full English words Drops the 4-letter alias convention in favour of a single homogeneous naming axis: full English words across filesystem, alias, wire format and constants. Module renames: - arts/aapp → arts/active-app (libs/aapp also) - arts/buss → arts/bus (libs/buss also) - arts/cach → arts/cache (libs/cach + svrs/cach also) - arts/conn → arts/connection - arts/fend → arts/frontend - arts/fmts → arts/formats (curr→currency, nums→numbers, unts→units) - arts/logr → arts/logger (libs/logr also) - arts/perm → arts/permissions (libs/perm + svrs/perm also) - arts/sess → arts/session - arts/stor → arts/storage - arts/timr → arts/timer (libs/timers → libs/timer) Modules left as-is: auth, dom, errs, http, lang, sium (already match their canonical name or are proper names). Special case: `aapp` could not become `app` because `$app` is reserved by SvelteKit (`$app/stores`, `$app/navigation`, ...). Compromise: - Filesystem and alias use `active-app` / `$active-app`. - Constants and class names use `App` / `APP_*` (no `active-` prefix). The `active-` prefix only disambiguates the alias from SvelteKit's namespace; the module is App. Special case: `permissions` keeps the plural for filesystem/alias/wire but constants and classes use the singular `PERMISSION_*` / `Permission*` because they describe the concept ("a permission effect"), not the module collection. Constants follow the new module name in caps: `STORAGE_*`, `BUS_*`, `CACHE_*`, `CONNECTION_*`, `FORMATS_*`, `LOGGER_*`, `SESSION_*`, `TIMER_*`, etc. Module values: `STORAGE_MODULE = 'storage'`, `BUS_MODULE = 'bus'`, `APP_MODULE = 'app'`, `PERMISSION_MODULE = 'permissions'`, etc. Wire/code format moved accordingly: `'storage::*'`, `'bus::*'`, `'session::*'`, `'permissions::*'`, etc. Diagnostic event values updated: `'storage.error'`, `'bus.event.published'`, `'connection.auth_failed'`, etc. App events use `'app.*'`: `AAPP_EVENT_* → APP_EVENT_*` with values `'app.user.identity.changed'`. Class renames (where they used the abbreviation): - AappAlreadyCreatedError → AppAlreadyCreatedError - BussError* → BusError* (where applicable) - Cach* → Cache* - Conn* → Connection* (e.g. ConnDisposedError → ConnectionDisposedError; ConnConnection* collapsed to Connection*) - Logr*Error → Logger*Error - Sess* → Session* (SessInvalidSessionError → SessionInvalidError) - Stor* → Storage* - Timr* → Timer* (TimrInactiveTimerError → TimerInactiveError) - AuthCachPort → AuthCachePort - AuthClientCach* → AuthClientCache* - AuthPermPort → AuthPermissionsPort Property renames in option types: - `cach?:` → `cache?:` in AuthClient options - `logr:` → `logger:` in svrs/auth ports - `timr:` → `timer:` in svrs/auth ports `docs/conventions.md` rewritten: - Rule 1 dropped the 4-letter alias mandate; lists the full English module names and special-cases active-app, lang, sium, permissions. - Rule 2 documents the new constant prefix convention and its two exceptions (APP_* for active-app, PERMISSION_* singular for permissions). - Rule 6 codifies that all error infrastructure (codes, messages, classes, guards) lives in a single `errors.ts` per module — removing the `consts.ts` / `errors.ts` split for error-related symbols. `libs/errs` adds `ErrorMessages` type so every module can declare its catalog as `<MOD>_ERROR_MESSAGES: ErrorMessages` instead of repeating the `Readonly<Record<ErrCode, string | (...args) => string>>` shape. Storage migrated as the first proof of the canonical pattern. All 1334 tests pass. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
logger,
timer: clock,
crypto,
cach,
mailer,
passwordHasher: createTestPasswordHasher()
}
});
Rename modules from 4-letter aliases to full English words Drops the 4-letter alias convention in favour of a single homogeneous naming axis: full English words across filesystem, alias, wire format and constants. Module renames: - arts/aapp → arts/active-app (libs/aapp also) - arts/buss → arts/bus (libs/buss also) - arts/cach → arts/cache (libs/cach + svrs/cach also) - arts/conn → arts/connection - arts/fend → arts/frontend - arts/fmts → arts/formats (curr→currency, nums→numbers, unts→units) - arts/logr → arts/logger (libs/logr also) - arts/perm → arts/permissions (libs/perm + svrs/perm also) - arts/sess → arts/session - arts/stor → arts/storage - arts/timr → arts/timer (libs/timers → libs/timer) Modules left as-is: auth, dom, errs, http, lang, sium (already match their canonical name or are proper names). Special case: `aapp` could not become `app` because `$app` is reserved by SvelteKit (`$app/stores`, `$app/navigation`, ...). Compromise: - Filesystem and alias use `active-app` / `$active-app`. - Constants and class names use `App` / `APP_*` (no `active-` prefix). The `active-` prefix only disambiguates the alias from SvelteKit's namespace; the module is App. Special case: `permissions` keeps the plural for filesystem/alias/wire but constants and classes use the singular `PERMISSION_*` / `Permission*` because they describe the concept ("a permission effect"), not the module collection. Constants follow the new module name in caps: `STORAGE_*`, `BUS_*`, `CACHE_*`, `CONNECTION_*`, `FORMATS_*`, `LOGGER_*`, `SESSION_*`, `TIMER_*`, etc. Module values: `STORAGE_MODULE = 'storage'`, `BUS_MODULE = 'bus'`, `APP_MODULE = 'app'`, `PERMISSION_MODULE = 'permissions'`, etc. Wire/code format moved accordingly: `'storage::*'`, `'bus::*'`, `'session::*'`, `'permissions::*'`, etc. Diagnostic event values updated: `'storage.error'`, `'bus.event.published'`, `'connection.auth_failed'`, etc. App events use `'app.*'`: `AAPP_EVENT_* → APP_EVENT_*` with values `'app.user.identity.changed'`. Class renames (where they used the abbreviation): - AappAlreadyCreatedError → AppAlreadyCreatedError - BussError* → BusError* (where applicable) - Cach* → Cache* - Conn* → Connection* (e.g. ConnDisposedError → ConnectionDisposedError; ConnConnection* collapsed to Connection*) - Logr*Error → Logger*Error - Sess* → Session* (SessInvalidSessionError → SessionInvalidError) - Stor* → Storage* - Timr* → Timer* (TimrInactiveTimerError → TimerInactiveError) - AuthCachPort → AuthCachePort - AuthClientCach* → AuthClientCache* - AuthPermPort → AuthPermissionsPort Property renames in option types: - `cach?:` → `cache?:` in AuthClient options - `logr:` → `logger:` in svrs/auth ports - `timr:` → `timer:` in svrs/auth ports `docs/conventions.md` rewritten: - Rule 1 dropped the 4-letter alias mandate; lists the full English module names and special-cases active-app, lang, sium, permissions. - Rule 2 documents the new constant prefix convention and its two exceptions (APP_* for active-app, PERMISSION_* singular for permissions). - Rule 6 codifies that all error infrastructure (codes, messages, classes, guards) lives in a single `errors.ts` per module — removing the `consts.ts` / `errors.ts` split for error-related symbols. `libs/errs` adds `ErrorMessages` type so every module can declare its catalog as `<MOD>_ERROR_MESSAGES: ErrorMessages` instead of repeating the `Readonly<Record<ErrCode, string | (...args) => string>>` shape. Storage migrated as the first proof of the canonical pattern. All 1334 tests pass. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
return { engine, store, actors, sess, logger, cach, mailer, clock };
}
async function snapshot(sessionId?: AuthSessionId) {
const current = await harness.engine.current({
tenantId: AUTH_TEST_TENANT_ID,
sessionId
});
const store = harness.store.snapshot();
return {
current,
counts: {
actors: harness.actors.snapshot().length,
credentials: store.credentials.length,
sessions: store.sessionBindings.length,
devices: store.devices.length,
flows: store.flows.length,
events: harness.logr.entries.length,
Rename modules from 4-letter aliases to full English words Drops the 4-letter alias convention in favour of a single homogeneous naming axis: full English words across filesystem, alias, wire format and constants. Module renames: - arts/aapp → arts/active-app (libs/aapp also) - arts/buss → arts/bus (libs/buss also) - arts/cach → arts/cache (libs/cach + svrs/cach also) - arts/conn → arts/connection - arts/fend → arts/frontend - arts/fmts → arts/formats (curr→currency, nums→numbers, unts→units) - arts/logr → arts/logger (libs/logr also) - arts/perm → arts/permissions (libs/perm + svrs/perm also) - arts/sess → arts/session - arts/stor → arts/storage - arts/timr → arts/timer (libs/timers → libs/timer) Modules left as-is: auth, dom, errs, http, lang, sium (already match their canonical name or are proper names). Special case: `aapp` could not become `app` because `$app` is reserved by SvelteKit (`$app/stores`, `$app/navigation`, ...). Compromise: - Filesystem and alias use `active-app` / `$active-app`. - Constants and class names use `App` / `APP_*` (no `active-` prefix). The `active-` prefix only disambiguates the alias from SvelteKit's namespace; the module is App. Special case: `permissions` keeps the plural for filesystem/alias/wire but constants and classes use the singular `PERMISSION_*` / `Permission*` because they describe the concept ("a permission effect"), not the module collection. Constants follow the new module name in caps: `STORAGE_*`, `BUS_*`, `CACHE_*`, `CONNECTION_*`, `FORMATS_*`, `LOGGER_*`, `SESSION_*`, `TIMER_*`, etc. Module values: `STORAGE_MODULE = 'storage'`, `BUS_MODULE = 'bus'`, `APP_MODULE = 'app'`, `PERMISSION_MODULE = 'permissions'`, etc. Wire/code format moved accordingly: `'storage::*'`, `'bus::*'`, `'session::*'`, `'permissions::*'`, etc. Diagnostic event values updated: `'storage.error'`, `'bus.event.published'`, `'connection.auth_failed'`, etc. App events use `'app.*'`: `AAPP_EVENT_* → APP_EVENT_*` with values `'app.user.identity.changed'`. Class renames (where they used the abbreviation): - AappAlreadyCreatedError → AppAlreadyCreatedError - BussError* → BusError* (where applicable) - Cach* → Cache* - Conn* → Connection* (e.g. ConnDisposedError → ConnectionDisposedError; ConnConnection* collapsed to Connection*) - Logr*Error → Logger*Error - Sess* → Session* (SessInvalidSessionError → SessionInvalidError) - Stor* → Storage* - Timr* → Timer* (TimrInactiveTimerError → TimerInactiveError) - AuthCachPort → AuthCachePort - AuthClientCach* → AuthClientCache* - AuthPermPort → AuthPermissionsPort Property renames in option types: - `cach?:` → `cache?:` in AuthClient options - `logr:` → `logger:` in svrs/auth ports - `timr:` → `timer:` in svrs/auth ports `docs/conventions.md` rewritten: - Rule 1 dropped the 4-letter alias mandate; lists the full English module names and special-cases active-app, lang, sium, permissions. - Rule 2 documents the new constant prefix convention and its two exceptions (APP_* for active-app, PERMISSION_* singular for permissions). - Rule 6 codifies that all error infrastructure (codes, messages, classes, guards) lives in a single `errors.ts` per module — removing the `consts.ts` / `errors.ts` split for error-related symbols. `libs/errs` adds `ErrorMessages` type so every module can declare its catalog as `<MOD>_ERROR_MESSAGES: ErrorMessages` instead of repeating the `Readonly<Record<ErrCode, string | (...args) => string>>` shape. Storage migrated as the first proof of the canonical pattern. All 1334 tests pass. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
cacheInvalidations: harness.cache.invalidations.length,
mails: harness.mailer.messages.length
},
events: harness.logr.entries.slice(-10).map((entry) => ({
level: entry.level,
category: entry.category,
message: entry.message,
eventName: entry.eventName,
code: entry.code,
actorId: entry.actorRef?.actorId,
sessionId: entry.sessionId
})),
Rename modules from 4-letter aliases to full English words Drops the 4-letter alias convention in favour of a single homogeneous naming axis: full English words across filesystem, alias, wire format and constants. Module renames: - arts/aapp → arts/active-app (libs/aapp also) - arts/buss → arts/bus (libs/buss also) - arts/cach → arts/cache (libs/cach + svrs/cach also) - arts/conn → arts/connection - arts/fend → arts/frontend - arts/fmts → arts/formats (curr→currency, nums→numbers, unts→units) - arts/logr → arts/logger (libs/logr also) - arts/perm → arts/permissions (libs/perm + svrs/perm also) - arts/sess → arts/session - arts/stor → arts/storage - arts/timr → arts/timer (libs/timers → libs/timer) Modules left as-is: auth, dom, errs, http, lang, sium (already match their canonical name or are proper names). Special case: `aapp` could not become `app` because `$app` is reserved by SvelteKit (`$app/stores`, `$app/navigation`, ...). Compromise: - Filesystem and alias use `active-app` / `$active-app`. - Constants and class names use `App` / `APP_*` (no `active-` prefix). The `active-` prefix only disambiguates the alias from SvelteKit's namespace; the module is App. Special case: `permissions` keeps the plural for filesystem/alias/wire but constants and classes use the singular `PERMISSION_*` / `Permission*` because they describe the concept ("a permission effect"), not the module collection. Constants follow the new module name in caps: `STORAGE_*`, `BUS_*`, `CACHE_*`, `CONNECTION_*`, `FORMATS_*`, `LOGGER_*`, `SESSION_*`, `TIMER_*`, etc. Module values: `STORAGE_MODULE = 'storage'`, `BUS_MODULE = 'bus'`, `APP_MODULE = 'app'`, `PERMISSION_MODULE = 'permissions'`, etc. Wire/code format moved accordingly: `'storage::*'`, `'bus::*'`, `'session::*'`, `'permissions::*'`, etc. Diagnostic event values updated: `'storage.error'`, `'bus.event.published'`, `'connection.auth_failed'`, etc. App events use `'app.*'`: `AAPP_EVENT_* → APP_EVENT_*` with values `'app.user.identity.changed'`. Class renames (where they used the abbreviation): - AappAlreadyCreatedError → AppAlreadyCreatedError - BussError* → BusError* (where applicable) - Cach* → Cache* - Conn* → Connection* (e.g. ConnDisposedError → ConnectionDisposedError; ConnConnection* collapsed to Connection*) - Logr*Error → Logger*Error - Sess* → Session* (SessInvalidSessionError → SessionInvalidError) - Stor* → Storage* - Timr* → Timer* (TimrInactiveTimerError → TimerInactiveError) - AuthCachPort → AuthCachePort - AuthClientCach* → AuthClientCache* - AuthPermPort → AuthPermissionsPort Property renames in option types: - `cach?:` → `cache?:` in AuthClient options - `logr:` → `logger:` in svrs/auth ports - `timr:` → `timer:` in svrs/auth ports `docs/conventions.md` rewritten: - Rule 1 dropped the 4-letter alias mandate; lists the full English module names and special-cases active-app, lang, sium, permissions. - Rule 2 documents the new constant prefix convention and its two exceptions (APP_* for active-app, PERMISSION_* singular for permissions). - Rule 6 codifies that all error infrastructure (codes, messages, classes, guards) lives in a single `errors.ts` per module — removing the `consts.ts` / `errors.ts` split for error-related symbols. `libs/errs` adds `ErrorMessages` type so every module can declare its catalog as `<MOD>_ERROR_MESSAGES: ErrorMessages` instead of repeating the `Readonly<Record<ErrCode, string | (...args) => string>>` shape. Storage migrated as the first proof of the canonical pattern. All 1334 tests pass. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
cacheInvalidations: harness.cache.invalidations.slice(-10),
devices: store.devices.slice(-10),
credentials: store.credentials.map((credential) => ({
id: credential.id,
kind: credential.kind,
identifierDisplay: credential.identifierDisplay,
actorId: credential.actorRef.actorId,
verifiedAt: credential.verifiedAt
}))
};
}
async function runAuthAction(
cookies: TestCookies,
action: string,
run: () => Promise<Record<string, unknown>>
) {
try {
const result = await run();
return {
ok: true,
action,
...result,
state: await snapshot(readSessionCookie(cookies))
};
} catch (error) {
return {
ok: false,
action,
error: errorToPayload(error),
state: await snapshot(readSessionCookie(cookies))
};
}
}
async function readPasswordForm(request: Request): Promise<PasswordFormInput> {
const form = await request.formData();
return {
identifier: String(form.get(AUTH_TEST_FORM_FIELDS.IDENTIFIER) ?? ''),
password: String(form.get(AUTH_TEST_FORM_FIELDS.PASSWORD) ?? ''),
displayName: String(form.get(AUTH_TEST_FORM_FIELDS.DISPLAY_NAME) ?? '')
};
}
function readSessionCookie(cookies: TestCookies): AuthSessionId | undefined {
return cookies.get(AUTH_TEST_COOKIE_NAMES.SESSION) as AuthSessionId | undefined;
}
function writeSessionCookie(cookies: TestCookies, sessionId: AuthSessionId | undefined): void {
if (!sessionId) return;
cookies.set(AUTH_TEST_COOKIE_NAMES.SESSION, sessionId, {
path: '/test/auth',
httpOnly: true,
sameSite: 'lax',
secure: false,
maxAge: AUTH_TEST_SESSION_COOKIE_MAX_AGE
});
}
function clearSessionCookie(cookies: TestCookies): void {
cookies.delete(AUTH_TEST_COOKIE_NAMES.SESSION, { path: '/test/auth' });
}
function errorToPayload(error: unknown) {
return {
name: errorName(error),
message:
error instanceof Error
? error.message
: typeof error === 'string'
? error
: 'Unknown auth error',
code: typeof error === 'object' && error && 'code' in error ? String(error.code) : undefined
};
}
function errorName(error: unknown): string {
return error instanceof Error ? error.name : typeof error;
}

Powered by TurnKey Linux.