@webstir-io/webstir-backend 0.1.15 → 0.1.16

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 (123) hide show
  1. package/README.md +106 -79
  2. package/dist/add.d.ts +59 -0
  3. package/dist/add.js +626 -0
  4. package/dist/build/artifacts.d.ts +115 -1
  5. package/dist/build/artifacts.js +4 -4
  6. package/dist/build/entries.js +1 -1
  7. package/dist/build/pipeline.d.ts +33 -1
  8. package/dist/build/pipeline.js +307 -65
  9. package/dist/cache/diff.js +9 -8
  10. package/dist/cache/reporters.js +1 -1
  11. package/dist/deploy-cli.d.ts +2 -0
  12. package/dist/deploy-cli.js +86 -0
  13. package/dist/diagnostics/summary.js +2 -2
  14. package/dist/index.d.ts +6 -0
  15. package/dist/index.js +4 -0
  16. package/dist/manifest/pipeline.js +103 -32
  17. package/dist/provider.js +35 -17
  18. package/dist/runtime/bun.d.ts +51 -0
  19. package/dist/runtime/bun.js +499 -0
  20. package/dist/runtime/core.d.ts +141 -0
  21. package/dist/runtime/core.js +316 -0
  22. package/dist/runtime/deploy-backend.d.ts +20 -0
  23. package/dist/runtime/deploy-backend.js +175 -0
  24. package/dist/runtime/deploy-shared.d.ts +43 -0
  25. package/dist/runtime/deploy-shared.js +75 -0
  26. package/dist/runtime/deploy-static.d.ts +2 -0
  27. package/dist/runtime/deploy-static.js +161 -0
  28. package/dist/runtime/deploy.d.ts +3 -0
  29. package/dist/runtime/deploy.js +91 -0
  30. package/dist/runtime/forms.d.ts +73 -0
  31. package/dist/runtime/forms.js +236 -0
  32. package/dist/runtime/request-hooks.d.ts +47 -0
  33. package/dist/runtime/request-hooks.js +102 -0
  34. package/dist/runtime/session-metadata.d.ts +13 -0
  35. package/dist/runtime/session-metadata.js +98 -0
  36. package/dist/runtime/session-runtime.d.ts +28 -0
  37. package/dist/runtime/session-runtime.js +180 -0
  38. package/dist/runtime/session.d.ts +83 -0
  39. package/dist/runtime/session.js +396 -0
  40. package/dist/runtime/views.d.ts +74 -0
  41. package/dist/runtime/views.js +221 -0
  42. package/dist/scaffold/assets.js +25 -21
  43. package/dist/testing/context.js +1 -1
  44. package/dist/testing/index.d.ts +1 -1
  45. package/dist/testing/index.js +100 -56
  46. package/dist/utils/bun.d.ts +2 -0
  47. package/dist/utils/bun.js +13 -0
  48. package/dist/watch.d.ts +13 -1
  49. package/dist/watch.js +345 -97
  50. package/dist/workspace.d.ts +8 -0
  51. package/dist/workspace.js +44 -3
  52. package/package.json +49 -14
  53. package/scripts/publish.sh +2 -92
  54. package/scripts/smoke.mjs +282 -107
  55. package/scripts/update-contract.sh +12 -10
  56. package/src/add.ts +964 -0
  57. package/src/build/artifacts.ts +49 -46
  58. package/src/build/entries.ts +12 -12
  59. package/src/build/pipeline.ts +779 -403
  60. package/src/cache/diff.ts +111 -105
  61. package/src/cache/reporters.ts +26 -26
  62. package/src/deploy-cli.ts +111 -0
  63. package/src/diagnostics/summary.ts +28 -22
  64. package/src/index.ts +11 -0
  65. package/src/manifest/pipeline.ts +328 -215
  66. package/src/provider.ts +115 -98
  67. package/src/runtime/bun.ts +793 -0
  68. package/src/runtime/core.ts +598 -0
  69. package/src/runtime/deploy-backend.ts +239 -0
  70. package/src/runtime/deploy-shared.ts +136 -0
  71. package/src/runtime/deploy-static.ts +191 -0
  72. package/src/runtime/deploy.ts +143 -0
  73. package/src/runtime/forms.ts +364 -0
  74. package/src/runtime/request-hooks.ts +165 -0
  75. package/src/runtime/session-metadata.ts +135 -0
  76. package/src/runtime/session-runtime.ts +267 -0
  77. package/src/runtime/session.ts +642 -0
  78. package/src/runtime/views.ts +385 -0
  79. package/src/scaffold/assets.ts +77 -73
  80. package/src/testing/context.js +8 -9
  81. package/src/testing/context.ts +9 -9
  82. package/src/testing/index.d.ts +14 -3
  83. package/src/testing/index.js +254 -175
  84. package/src/testing/index.ts +298 -195
  85. package/src/testing/types.d.ts +18 -19
  86. package/src/testing/types.ts +18 -18
  87. package/src/utils/bun.ts +26 -0
  88. package/src/watch.ts +503 -99
  89. package/src/workspace.ts +59 -3
  90. package/templates/backend/.env.example +15 -0
  91. package/templates/backend/auth/adapter.ts +335 -36
  92. package/templates/backend/db/connection.ts +190 -65
  93. package/templates/backend/db/migrate.ts +149 -43
  94. package/templates/backend/db/types.d.ts +1 -1
  95. package/templates/backend/env.ts +132 -20
  96. package/templates/backend/functions/hello/index.ts +1 -2
  97. package/templates/backend/index.ts +15 -508
  98. package/templates/backend/jobs/nightly/index.ts +1 -1
  99. package/templates/backend/jobs/runtime.ts +24 -11
  100. package/templates/backend/jobs/scheduler.ts +208 -46
  101. package/templates/backend/module.ts +227 -13
  102. package/templates/backend/observability/logger.ts +2 -12
  103. package/templates/backend/observability/metrics.ts +8 -5
  104. package/templates/backend/session/sqlite.ts +152 -0
  105. package/templates/backend/session/store.ts +45 -0
  106. package/templates/backend/tsconfig.json +1 -1
  107. package/tests/add.test.js +327 -0
  108. package/tests/authAdapter.test.js +315 -0
  109. package/tests/bundlerParity.test.js +217 -0
  110. package/tests/cacheReporter.test.js +10 -10
  111. package/tests/dbConnection.test.js +209 -0
  112. package/tests/deploy.test.js +357 -0
  113. package/tests/envLoader.test.js +271 -17
  114. package/tests/integration.test.js +2432 -3
  115. package/tests/jobsScheduler.test.js +253 -0
  116. package/tests/manifest.test.js +287 -12
  117. package/tests/migrationRunner.test.js +249 -0
  118. package/tests/sessionScaffoldStore.test.js +752 -0
  119. package/tests/sessionStore.test.js +490 -0
  120. package/tests/testing.test.js +252 -0
  121. package/tests/watch.test.js +192 -32
  122. package/tsconfig.json +3 -10
  123. package/templates/backend/server/fastify.ts +0 -288
@@ -0,0 +1,102 @@
1
+ export function resolveRequestHooks(options) {
2
+ const definitions = new Map();
3
+ for (const definition of options.manifestDefinitions ?? []) {
4
+ if (!definition?.id) {
5
+ continue;
6
+ }
7
+ definitions.set(definition.id, definition);
8
+ }
9
+ const registrations = new Map();
10
+ for (const registration of options.registrations ?? []) {
11
+ if (!registration?.id || typeof registration.handler !== 'function') {
12
+ continue;
13
+ }
14
+ registrations.set(registration.id, registration.handler);
15
+ }
16
+ const hooks = [];
17
+ const warnings = [];
18
+ const seen = new Set();
19
+ for (const reference of options.routeReferences ?? []) {
20
+ const hookId = reference?.id?.trim();
21
+ if (!hookId || seen.has(hookId)) {
22
+ continue;
23
+ }
24
+ seen.add(hookId);
25
+ const definition = definitions.get(hookId);
26
+ if (!definition?.phase) {
27
+ warnings.push(`Route "${options.routeName}" references request hook "${hookId}" without manifest metadata.`);
28
+ continue;
29
+ }
30
+ const handler = registrations.get(hookId);
31
+ if (!handler) {
32
+ warnings.push(`Route "${options.routeName}" references request hook "${hookId}" without an implementation.`);
33
+ continue;
34
+ }
35
+ hooks.push({
36
+ id: hookId,
37
+ phase: definition.phase,
38
+ order: Number.isInteger(definition.order) ? Number(definition.order) : 0,
39
+ handler,
40
+ });
41
+ }
42
+ hooks.sort((left, right) => {
43
+ const phaseDelta = compareRequestHookPhase(left.phase, right.phase);
44
+ if (phaseDelta !== 0) {
45
+ return phaseDelta;
46
+ }
47
+ if (left.order !== right.order) {
48
+ return left.order - right.order;
49
+ }
50
+ return left.id.localeCompare(right.id);
51
+ });
52
+ return { hooks, warnings };
53
+ }
54
+ export async function executeRequestHookPhase(options) {
55
+ let currentResult = options.result;
56
+ for (const hook of options.hooks) {
57
+ if (hook.phase !== options.phase) {
58
+ continue;
59
+ }
60
+ try {
61
+ const hookResult = await hook.handler(options.context, {
62
+ phase: options.phase,
63
+ route: options.route,
64
+ result: currentResult,
65
+ });
66
+ if (options.phase === 'afterHandler') {
67
+ if (hookResult !== undefined) {
68
+ currentResult = hookResult;
69
+ }
70
+ continue;
71
+ }
72
+ if (hookResult !== undefined) {
73
+ options.logger?.info?.('request hook produced early response', {
74
+ hookId: hook.id,
75
+ phase: hook.phase,
76
+ });
77
+ return { result: hookResult, shortCircuited: true };
78
+ }
79
+ }
80
+ catch (error) {
81
+ options.logger?.error?.('request hook failed', {
82
+ err: error,
83
+ hookId: hook.id,
84
+ phase: hook.phase,
85
+ });
86
+ throw error;
87
+ }
88
+ }
89
+ return { result: currentResult, shortCircuited: false };
90
+ }
91
+ function compareRequestHookPhase(left, right) {
92
+ return requestHookPhaseWeight(left) - requestHookPhaseWeight(right);
93
+ }
94
+ function requestHookPhaseWeight(phase) {
95
+ if (phase === 'beforeAuth') {
96
+ return 0;
97
+ }
98
+ if (phase === 'beforeHandler') {
99
+ return 1;
100
+ }
101
+ return 2;
102
+ }
@@ -0,0 +1,13 @@
1
+ export interface SessionMetadata {
2
+ id: string;
3
+ createdAt: string;
4
+ expiresAt: string;
5
+ }
6
+ export interface SessionMetadataInput {
7
+ id?: string;
8
+ createdAt?: string;
9
+ expiresAt?: string;
10
+ }
11
+ export declare function attachSessionMetadata<TSession extends Record<string, unknown>>(session: TSession, metadata: SessionMetadata | undefined): TSession;
12
+ export declare function readSessionMetadata(session: Record<string, unknown> | null | undefined): SessionMetadataInput | undefined;
13
+ export declare function stripSessionMetadataFields(session: Record<string, unknown>): void;
@@ -0,0 +1,98 @@
1
+ const SESSION_METADATA_KEY = Symbol.for('webstir.webstir-backend.session-metadata');
2
+ const SESSION_METADATA_FIELDS = ['id', 'createdAt', 'expiresAt'];
3
+ export function attachSessionMetadata(session, metadata) {
4
+ const normalized = cloneSessionMetadata(metadata);
5
+ if (!normalized) {
6
+ return session;
7
+ }
8
+ Object.defineProperty(session, SESSION_METADATA_KEY, {
9
+ configurable: true,
10
+ enumerable: false,
11
+ value: normalized,
12
+ writable: true,
13
+ });
14
+ for (const field of SESSION_METADATA_FIELDS) {
15
+ Object.defineProperty(session, field, {
16
+ configurable: true,
17
+ enumerable: false,
18
+ value: normalized[field],
19
+ writable: true,
20
+ });
21
+ }
22
+ return session;
23
+ }
24
+ export function readSessionMetadata(session) {
25
+ if (!session || !isRecord(session)) {
26
+ return undefined;
27
+ }
28
+ const attached = readAttachedSessionMetadata(session);
29
+ const metadata = cloneSessionMetadataInput({
30
+ id: normalizeText(session.id) ?? attached?.id,
31
+ createdAt: normalizeDate(session.createdAt) ?? attached?.createdAt,
32
+ expiresAt: normalizeDate(session.expiresAt) ?? attached?.expiresAt,
33
+ });
34
+ return metadata && Object.keys(metadata).length > 0 ? metadata : undefined;
35
+ }
36
+ export function stripSessionMetadataFields(session) {
37
+ for (const field of SESSION_METADATA_FIELDS) {
38
+ Reflect.deleteProperty(session, field);
39
+ }
40
+ }
41
+ function readAttachedSessionMetadata(session) {
42
+ const attached = session[SESSION_METADATA_KEY];
43
+ return cloneSessionMetadata(attached);
44
+ }
45
+ function cloneSessionMetadata(value) {
46
+ if (!isRecord(value)) {
47
+ return undefined;
48
+ }
49
+ const metadata = cloneSessionMetadataInput(value);
50
+ const id = metadata?.id;
51
+ const createdAt = metadata?.createdAt;
52
+ const expiresAt = metadata?.expiresAt;
53
+ if (!id || !createdAt || !expiresAt) {
54
+ return undefined;
55
+ }
56
+ return {
57
+ id,
58
+ createdAt,
59
+ expiresAt,
60
+ };
61
+ }
62
+ function cloneSessionMetadataInput(value) {
63
+ if (!isRecord(value)) {
64
+ return undefined;
65
+ }
66
+ const metadata = {};
67
+ const id = normalizeText(value.id);
68
+ const createdAt = normalizeDate(value.createdAt);
69
+ const expiresAt = normalizeDate(value.expiresAt);
70
+ if (id) {
71
+ metadata.id = id;
72
+ }
73
+ if (createdAt) {
74
+ metadata.createdAt = createdAt;
75
+ }
76
+ if (expiresAt) {
77
+ metadata.expiresAt = expiresAt;
78
+ }
79
+ return metadata;
80
+ }
81
+ function normalizeText(value) {
82
+ return typeof value === 'string' && value.trim().length > 0 ? value.trim() : undefined;
83
+ }
84
+ function normalizeDate(value) {
85
+ if (value instanceof Date && !Number.isNaN(value.getTime())) {
86
+ return value.toISOString();
87
+ }
88
+ if (typeof value === 'string') {
89
+ const timestamp = Date.parse(value);
90
+ if (!Number.isNaN(timestamp)) {
91
+ return new Date(timestamp).toISOString();
92
+ }
93
+ }
94
+ return undefined;
95
+ }
96
+ function isRecord(value) {
97
+ return Boolean(value && typeof value === 'object' && !Array.isArray(value));
98
+ }
@@ -0,0 +1,28 @@
1
+ export type SessionRuntimeFormIssueCode = 'validation' | 'auth' | 'csrf';
2
+ export type SessionRuntimeFormValue = string | string[];
3
+ export type SessionRuntimeFormValues = Record<string, SessionRuntimeFormValue>;
4
+ export interface SessionRuntimeFormIssue {
5
+ code?: SessionRuntimeFormIssueCode;
6
+ field?: string;
7
+ message: string;
8
+ }
9
+ export interface SessionRuntimeStoredFormState {
10
+ values: SessionRuntimeFormValues;
11
+ issues: SessionRuntimeFormIssue[];
12
+ createdAt: string;
13
+ }
14
+ export interface SessionRuntimeFormState {
15
+ csrf: Record<string, string>;
16
+ states: Record<string, SessionRuntimeStoredFormState>;
17
+ }
18
+ export interface SessionRuntimeState {
19
+ form?: SessionRuntimeFormState;
20
+ }
21
+ export declare function attachSessionRuntimeState<TSession extends Record<string, unknown>>(session: TSession, runtime: SessionRuntimeState | undefined): TSession;
22
+ export declare function cloneSessionRuntimeState(runtime: SessionRuntimeState | undefined): SessionRuntimeState | undefined;
23
+ export declare function coerceSessionRuntimeFormState(value: unknown): SessionRuntimeFormState | undefined;
24
+ export declare function getFormSessionRuntimeState(session: Record<string, unknown>): SessionRuntimeFormState;
25
+ export declare function hasSessionRuntimeState(runtime: SessionRuntimeState | undefined): boolean;
26
+ export declare function mergeSessionRuntimeState(left: SessionRuntimeState | undefined, right: SessionRuntimeState | undefined): SessionRuntimeState | undefined;
27
+ export declare function pruneSessionRuntimeState(session: Record<string, unknown>): void;
28
+ export declare function readSessionRuntimeState(session: Record<string, unknown> | null | undefined): SessionRuntimeState | undefined;
@@ -0,0 +1,180 @@
1
+ const SESSION_RUNTIME_KEY = Symbol.for('webstir.webstir-backend.session-runtime');
2
+ const EPOCH_ISO = '1970-01-01T00:00:00.000Z';
3
+ export function attachSessionRuntimeState(session, runtime) {
4
+ const normalized = cloneSessionRuntimeState(runtime);
5
+ if (!hasSessionRuntimeState(normalized)) {
6
+ Reflect.deleteProperty(session, SESSION_RUNTIME_KEY);
7
+ return session;
8
+ }
9
+ Object.defineProperty(session, SESSION_RUNTIME_KEY, {
10
+ configurable: true,
11
+ enumerable: false,
12
+ value: normalized,
13
+ writable: true,
14
+ });
15
+ return session;
16
+ }
17
+ export function cloneSessionRuntimeState(runtime) {
18
+ if (!runtime || !isRecord(runtime)) {
19
+ return undefined;
20
+ }
21
+ const form = cloneSessionRuntimeFormState(runtime.form);
22
+ if (!form) {
23
+ return undefined;
24
+ }
25
+ return { form };
26
+ }
27
+ export function coerceSessionRuntimeFormState(value) {
28
+ return cloneSessionRuntimeFormState(value);
29
+ }
30
+ export function getFormSessionRuntimeState(session) {
31
+ const existing = readAttachedSessionRuntimeState(session);
32
+ if (existing?.form) {
33
+ existing.form.csrf ??= {};
34
+ existing.form.states ??= {};
35
+ return existing.form;
36
+ }
37
+ const created = {
38
+ csrf: {},
39
+ states: {},
40
+ };
41
+ const runtime = {
42
+ ...(existing ?? {}),
43
+ form: created,
44
+ };
45
+ Object.defineProperty(session, SESSION_RUNTIME_KEY, {
46
+ configurable: true,
47
+ enumerable: false,
48
+ value: runtime,
49
+ writable: true,
50
+ });
51
+ return created;
52
+ }
53
+ export function hasSessionRuntimeState(runtime) {
54
+ if (!runtime?.form) {
55
+ return false;
56
+ }
57
+ return (Object.keys(runtime.form.csrf ?? {}).length > 0 ||
58
+ Object.keys(runtime.form.states ?? {}).length > 0);
59
+ }
60
+ export function mergeSessionRuntimeState(left, right) {
61
+ const leftClone = cloneSessionRuntimeState(left);
62
+ const rightClone = cloneSessionRuntimeState(right);
63
+ if (!leftClone) {
64
+ return rightClone;
65
+ }
66
+ if (!rightClone) {
67
+ return leftClone;
68
+ }
69
+ return {
70
+ form: leftClone.form || rightClone.form
71
+ ? {
72
+ csrf: {
73
+ ...(leftClone.form?.csrf ?? {}),
74
+ ...(rightClone.form?.csrf ?? {}),
75
+ },
76
+ states: {
77
+ ...(leftClone.form?.states ?? {}),
78
+ ...(rightClone.form?.states ?? {}),
79
+ },
80
+ }
81
+ : undefined,
82
+ };
83
+ }
84
+ export function pruneSessionRuntimeState(session) {
85
+ const runtime = readAttachedSessionRuntimeState(session);
86
+ if (!runtime) {
87
+ return;
88
+ }
89
+ if (runtime.form &&
90
+ Object.keys(runtime.form.csrf ?? {}).length === 0 &&
91
+ Object.keys(runtime.form.states ?? {}).length === 0) {
92
+ delete runtime.form;
93
+ }
94
+ if (!hasSessionRuntimeState(runtime)) {
95
+ Reflect.deleteProperty(session, SESSION_RUNTIME_KEY);
96
+ }
97
+ }
98
+ export function readSessionRuntimeState(session) {
99
+ if (!session || !isRecord(session)) {
100
+ return undefined;
101
+ }
102
+ return cloneSessionRuntimeState(readAttachedSessionRuntimeState(session));
103
+ }
104
+ function readAttachedSessionRuntimeState(session) {
105
+ const runtime = session[SESSION_RUNTIME_KEY];
106
+ return isRecord(runtime) ? runtime : undefined;
107
+ }
108
+ function cloneSessionRuntimeFormState(value) {
109
+ if (!isRecord(value)) {
110
+ return undefined;
111
+ }
112
+ return {
113
+ csrf: cloneSessionRuntimeCsrfState(value.csrf),
114
+ states: cloneSessionRuntimeStoredStateMap(value.states),
115
+ };
116
+ }
117
+ function cloneSessionRuntimeCsrfState(value) {
118
+ if (!isRecord(value)) {
119
+ return {};
120
+ }
121
+ return Object.fromEntries(Object.entries(value).flatMap(([key, candidate]) => typeof candidate === 'string' ? [[key, candidate]] : []));
122
+ }
123
+ function cloneSessionRuntimeStoredStateMap(value) {
124
+ if (!isRecord(value)) {
125
+ return {};
126
+ }
127
+ return Object.fromEntries(Object.entries(value).flatMap(([key, candidate]) => {
128
+ const state = cloneSessionRuntimeStoredFormState(candidate);
129
+ return state ? [[key, state]] : [];
130
+ }));
131
+ }
132
+ function cloneSessionRuntimeStoredFormState(value) {
133
+ if (!isRecord(value)) {
134
+ return undefined;
135
+ }
136
+ return {
137
+ values: cloneSessionRuntimeFormValues(value.values),
138
+ issues: cloneSessionRuntimeIssues(value.issues),
139
+ createdAt: typeof value.createdAt === 'string' ? value.createdAt : EPOCH_ISO,
140
+ };
141
+ }
142
+ function cloneSessionRuntimeFormValues(value) {
143
+ if (!isRecord(value)) {
144
+ return {};
145
+ }
146
+ const values = {};
147
+ for (const [key, candidate] of Object.entries(value)) {
148
+ if (typeof candidate === 'string') {
149
+ values[key] = candidate;
150
+ continue;
151
+ }
152
+ if (Array.isArray(candidate) && candidate.every((item) => typeof item === 'string')) {
153
+ values[key] = [...candidate];
154
+ }
155
+ }
156
+ return values;
157
+ }
158
+ function cloneSessionRuntimeIssues(value) {
159
+ if (!Array.isArray(value)) {
160
+ return [];
161
+ }
162
+ return value.flatMap((candidate) => {
163
+ if (!isRecord(candidate) || typeof candidate.message !== 'string') {
164
+ return [];
165
+ }
166
+ return [
167
+ {
168
+ code: normalizeIssueCode(candidate.code),
169
+ field: typeof candidate.field === 'string' ? candidate.field : undefined,
170
+ message: candidate.message,
171
+ },
172
+ ];
173
+ });
174
+ }
175
+ function normalizeIssueCode(value) {
176
+ return value === 'validation' || value === 'auth' || value === 'csrf' ? value : undefined;
177
+ }
178
+ function isRecord(value) {
179
+ return Boolean(value && typeof value === 'object' && !Array.isArray(value));
180
+ }
@@ -0,0 +1,83 @@
1
+ import { type SessionRuntimeState } from './session-runtime.js';
2
+ export type FlashLevel = 'info' | 'success' | 'warning' | 'error';
3
+ export type FlashPublishCondition = 'always' | 'success' | 'error';
4
+ export interface SessionCookieConfig {
5
+ secret: string;
6
+ cookieName: string;
7
+ secure: boolean;
8
+ maxAgeSeconds: number;
9
+ path?: string;
10
+ sameSite?: 'Lax' | 'Strict' | 'None';
11
+ }
12
+ export interface SessionFlashMessage {
13
+ key: string;
14
+ level: FlashLevel;
15
+ createdAt: string;
16
+ }
17
+ export interface RouteSessionDefinitionLike {
18
+ mode?: 'optional' | 'required';
19
+ write?: boolean;
20
+ }
21
+ export interface RouteFlashMessageDefinitionLike {
22
+ key?: string;
23
+ level?: FlashLevel;
24
+ when?: FlashPublishCondition;
25
+ }
26
+ export interface RouteFlashDefinitionLike {
27
+ consume?: readonly string[];
28
+ publish?: readonly RouteFlashMessageDefinitionLike[];
29
+ }
30
+ export interface RouteFormDefinitionLike {
31
+ session?: RouteSessionDefinitionLike;
32
+ flash?: RouteFlashDefinitionLike;
33
+ }
34
+ export interface SessionAwareRouteDefinitionLike {
35
+ session?: RouteSessionDefinitionLike;
36
+ flash?: RouteFlashDefinitionLike;
37
+ form?: RouteFormDefinitionLike;
38
+ }
39
+ export interface SessionCommitResult<TSession> {
40
+ session: TSession | null;
41
+ setCookie?: string;
42
+ }
43
+ export interface PreparedSessionState<TSession, TResult> {
44
+ session: TSession | null;
45
+ flash: SessionFlashMessage[];
46
+ commit(options: {
47
+ session: TSession | null;
48
+ route?: SessionAwareRouteDefinitionLike;
49
+ result?: TResult;
50
+ }): SessionCommitResult<TSession>;
51
+ }
52
+ export interface SessionStoreRuntimeState extends SessionRuntimeState {
53
+ flash?: SessionFlashMessage[];
54
+ }
55
+ export interface SessionStoreRecord<TSession extends Record<string, unknown> = Record<string, unknown>> {
56
+ id: string;
57
+ value: TSession;
58
+ flash?: SessionFlashMessage[];
59
+ runtime?: SessionStoreRuntimeState;
60
+ createdAt: string;
61
+ expiresAt: string;
62
+ }
63
+ export interface SessionStore<TSession extends Record<string, unknown> = Record<string, unknown>> {
64
+ get(sessionId: string): SessionStoreRecord<TSession> | undefined;
65
+ set(record: SessionStoreRecord<TSession>): void;
66
+ delete(sessionId: string): void;
67
+ }
68
+ export interface InMemorySessionStore<TSession extends Record<string, unknown> = Record<string, unknown>> extends SessionStore<TSession> {
69
+ clear(): void;
70
+ }
71
+ export declare function prepareSessionState<TSession extends Record<string, unknown>, TResult extends {
72
+ status?: number;
73
+ errors?: unknown;
74
+ }>(options: {
75
+ cookies?: Record<string, string> | string | string[];
76
+ route?: SessionAwareRouteDefinitionLike;
77
+ config: SessionCookieConfig;
78
+ store?: SessionStore<TSession>;
79
+ now?: () => Date;
80
+ }): PreparedSessionState<TSession, TResult>;
81
+ export declare function parseCookieHeader(header: string | string[] | undefined): Record<string, string>;
82
+ export declare function createInMemorySessionStore<TSession extends Record<string, unknown> = Record<string, unknown>>(): InMemorySessionStore<TSession>;
83
+ export declare function resetInMemorySessionStore(store?: InMemorySessionStore<Record<string, unknown>>): void;