@vielzeug/forge 1.1.2 → 1.2.1
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.
- package/dist/devtools.cjs +1 -1
- package/dist/devtools.cjs.map +1 -1
- package/dist/devtools.d.ts +5 -5
- package/dist/devtools.d.ts.map +1 -1
- package/dist/devtools.js +1 -1
- package/dist/devtools.js.map +1 -1
- package/package.json +8 -5
package/dist/devtools.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./_utils.cjs");let t=require("@vielzeug/arsenal");var n=!globalThis.__FORGE_PROD__;function r(r,i={}){if(!n)return()=>{};let a=`[forge:devtools:${i.label??`form`}]`,o=new Map,s=r.state.isSubmitting,c=r.state.isLoading;function l(e){let n=new Set(o.keys());for(let e of Object.keys((0,t.flattenPaths)(r.values())))n.add(e);for(let t of Object.keys(e.errors))n.add(t);for(let t of e.touchedFields)n.add(t);return n}function u(t){for(let n of l(t)){let t=r.field(n),i=o.get(n);if(o.set(n,t),!i)continue;let s=e.sanitizeForLog(n,80);i.value!==t.value&&console.debug(`${a} field "${s}" value:`,i.value,`→`,t.value),i.error!==t.error&&console.debug(`${a} field "${s}" error:`,i.error,`→`,t.error),i.touched!==t.touched&&console.debug(`${a} field "${s}" touched:`,t.touched),i.dirty!==t.dirty&&console.debug(`${a} field "${s}" dirty:`,t.dirty)}}return r.subscribe(e=>{u(e),e.isSubmitting!==s&&(s=e.isSubmitting,console.debug(`${a} submit ${e.isSubmitting?`started`:`ended`} (submitCount=${e.submitCount})`)),e.isLoading!==c&&(c=e.isLoading,console.debug(`${a} isLoading:`,e.isLoading))},{sync:!0})}exports.
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./_utils.cjs");let t=require("@vielzeug/arsenal");var n=!globalThis.__FORGE_PROD__;function r(r,i={}){if(!n)return()=>{};let a=`[forge:devtools:${i.label??`form`}]`,o=new Map,s=r.state.isSubmitting,c=r.state.isLoading;function l(e){let n=new Set(o.keys());for(let e of Object.keys((0,t.flattenPaths)(r.values())))n.add(e);for(let t of Object.keys(e.errors))n.add(t);for(let t of e.touchedFields)n.add(t);return n}function u(t){for(let n of l(t)){let t=r.field(n),i=o.get(n);if(o.set(n,t),!i)continue;let s=e.sanitizeForLog(n,80);i.value!==t.value&&console.debug(`${a} field "${s}" value:`,i.value,`→`,t.value),i.error!==t.error&&console.debug(`${a} field "${s}" error:`,i.error,`→`,t.error),i.touched!==t.touched&&console.debug(`${a} field "${s}" touched:`,t.touched),i.dirty!==t.dirty&&console.debug(`${a} field "${s}" dirty:`,t.dirty)}}return r.subscribe(e=>{u(e),e.isSubmitting!==s&&(s=e.isSubmitting,console.debug(`${a} submit ${e.isSubmitting?`started`:`ended`} (submitCount=${e.submitCount})`)),e.isLoading!==c&&(c=e.isLoading,console.debug(`${a} isLoading:`,e.isLoading))},{sync:!0})}exports.debugForm=r;
|
|
2
2
|
//# sourceMappingURL=devtools.cjs.map
|
package/dist/devtools.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"devtools.cjs","names":[],"sources":["../src/devtools.ts"],"sourcesContent":["/**\n * @vielzeug/forge — debug utilities for form state visualisation.\n *\n * Import from the dedicated sub-path so it is tree-shaken from production bundles:\n * ```ts\n * import {
|
|
1
|
+
{"version":3,"file":"devtools.cjs","names":[],"sources":["../src/devtools.ts"],"sourcesContent":["/**\n * @vielzeug/forge — debug utilities for form state visualisation.\n *\n * Import from the dedicated sub-path so it is tree-shaken from production bundles:\n * ```ts\n * import { debugForm } from '@vielzeug/forge/devtools';\n * ```\n */\n\nimport type { FieldState, FlatKeyOf, Form, FormState, Unsubscribe } from './types';\n\nimport { flattenValues, sanitizeForLog } from './_utils';\n\nconst isDev = !(globalThis as { __FORGE_PROD__?: boolean }).__FORGE_PROD__;\n\n/** Options for {@link debugForm}. */\nexport type ForgeDevtoolsOptions = {\n /** Label included in every log line, useful when debugging multiple forms at once. Default: `\"form\"`. */\n label?: string;\n};\n\n/**\n * Attaches a `console.debug`-based observer to `form` that logs a distinct line per observable\n * state transition: per-field value/error/touched/dirty changes, submit start/end (via the\n * `isSubmitting` edge), and `isLoading` edge (async `defaultValues` resolving).\n *\n * **Development only** by default — logging is a no-op when `__FORGE_PROD__` is set (the same\n * convention `_dev.ts` uses internally). Import from the dedicated `/devtools` sub-path so the\n * logging code is tree-shaken from production bundles entirely.\n *\n * Works identically on scoped sub-forms (`form.scope('address')`) — field paths logged are\n * relative to whatever `form` object is passed in, matching that form's own `state` convention.\n *\n * @example\n * ```ts\n * import { createForm } from '@vielzeug/forge';\n * import { debugForm } from '@vielzeug/forge/devtools';\n *\n * const form = createForm({ defaultValues: { email: '' } });\n * const detach = debugForm(form, { label: 'signup' });\n * // [forge:devtools:signup] field \"email\" value: \"\" → \"a@b.com\"\n *\n * detach(); // stop logging\n * ```\n */\nexport function debugForm<TValues extends Record<string, unknown>>(\n form: Form<TValues>,\n options: ForgeDevtoolsOptions = {},\n): Unsubscribe {\n if (!isDev) return () => {};\n\n const prefix = `[forge:devtools:${options.label ?? 'form'}]`;\n const fieldSnapshots = new Map<string, FieldState<unknown>>();\n\n let prevIsSubmitting = form.state.isSubmitting;\n let prevIsLoading = form.state.isLoading;\n\n function knownFieldKeys(state: FormState): Set<string> {\n const keys = new Set<string>(fieldSnapshots.keys());\n\n for (const key of Object.keys(flattenValues(form.values() as Record<string, unknown>))) keys.add(key);\n for (const key of Object.keys(state.errors)) keys.add(key);\n for (const key of state.touchedFields) keys.add(key);\n\n return keys;\n }\n\n function logFieldDiffs(state: FormState): void {\n for (const key of knownFieldKeys(state)) {\n const next = form.field(key as FlatKeyOf<TValues>);\n const prev = fieldSnapshots.get(key);\n\n fieldSnapshots.set(key, next);\n\n // First observation of this field establishes a baseline — logging it would just be\n // noise (every field would \"change\" from undefined on attach).\n if (!prev) continue;\n\n // Security: `key` is a caller-chosen field path (assertSafeKey only rejects reserved\n // segments, not control characters) — sanitize before interpolating into console output\n // to prevent terminal-escape-sequence injection, mirroring the sanitization `warn()`\n // call sites already apply (see `_utils.ts` sanitizeForLog).\n const safeKey = sanitizeForLog(key, 80);\n\n if (prev.value !== next.value) console.debug(`${prefix} field \"${safeKey}\" value:`, prev.value, '→', next.value);\n\n if (prev.error !== next.error) console.debug(`${prefix} field \"${safeKey}\" error:`, prev.error, '→', next.error);\n\n if (prev.touched !== next.touched) console.debug(`${prefix} field \"${safeKey}\" touched:`, next.touched);\n\n if (prev.dirty !== next.dirty) console.debug(`${prefix} field \"${safeKey}\" dirty:`, next.dirty);\n }\n }\n\n return form.subscribe(\n (state) => {\n logFieldDiffs(state);\n\n if (state.isSubmitting !== prevIsSubmitting) {\n prevIsSubmitting = state.isSubmitting;\n console.debug(\n `${prefix} submit ${state.isSubmitting ? 'started' : 'ended'} (submitCount=${state.submitCount})`,\n );\n }\n\n if (state.isLoading !== prevIsLoading) {\n prevIsLoading = state.isLoading;\n console.debug(`${prefix} isLoading:`, state.isLoading);\n }\n },\n { sync: true },\n );\n}\n"],"mappings":"sIAaA,IAAM,EAAQ,CAAE,WAA4C,eAgC5D,SAAgB,EACd,EACA,EAAgC,CAAC,EACpB,CACb,GAAI,CAAC,EAAO,UAAa,CAAC,EAE1B,IAAM,EAAS,mBAAmB,EAAQ,OAAS,OAAO,GACpD,EAAiB,IAAI,IAEvB,EAAmB,EAAK,MAAM,aAC9B,EAAgB,EAAK,MAAM,UAE/B,SAAS,EAAe,EAA+B,CACrD,IAAM,EAAO,IAAI,IAAY,EAAe,KAAK,CAAC,EAElD,IAAK,IAAM,KAAO,OAAO,MAAA,EAAA,EAAA,aAAA,CAAmB,EAAK,OAAO,CAA4B,CAAC,EAAG,EAAK,IAAI,CAAG,EACpG,IAAK,IAAM,KAAO,OAAO,KAAK,EAAM,MAAM,EAAG,EAAK,IAAI,CAAG,EACzD,IAAK,IAAM,KAAO,EAAM,cAAe,EAAK,IAAI,CAAG,EAEnD,OAAO,CACT,CAEA,SAAS,EAAc,EAAwB,CAC7C,IAAK,IAAM,KAAO,EAAe,CAAK,EAAG,CACvC,IAAM,EAAO,EAAK,MAAM,CAAyB,EAC3C,EAAO,EAAe,IAAI,CAAG,EAMnC,GAJA,EAAe,IAAI,EAAK,CAAI,EAIxB,CAAC,EAAM,SAMX,IAAM,EAAU,EAAA,eAAe,EAAK,EAAE,EAElC,EAAK,QAAU,EAAK,OAAO,QAAQ,MAAM,GAAG,EAAO,UAAU,EAAQ,UAAW,EAAK,MAAO,IAAK,EAAK,KAAK,EAE3G,EAAK,QAAU,EAAK,OAAO,QAAQ,MAAM,GAAG,EAAO,UAAU,EAAQ,UAAW,EAAK,MAAO,IAAK,EAAK,KAAK,EAE3G,EAAK,UAAY,EAAK,SAAS,QAAQ,MAAM,GAAG,EAAO,UAAU,EAAQ,YAAa,EAAK,OAAO,EAElG,EAAK,QAAU,EAAK,OAAO,QAAQ,MAAM,GAAG,EAAO,UAAU,EAAQ,UAAW,EAAK,KAAK,CAChG,CACF,CAEA,OAAO,EAAK,UACT,GAAU,CACT,EAAc,CAAK,EAEf,EAAM,eAAiB,IACzB,EAAmB,EAAM,aACzB,QAAQ,MACN,GAAG,EAAO,UAAU,EAAM,aAAe,UAAY,QAAQ,gBAAgB,EAAM,YAAY,EACjG,GAGE,EAAM,YAAc,IACtB,EAAgB,EAAM,UACtB,QAAQ,MAAM,GAAG,EAAO,aAAc,EAAM,SAAS,EAEzD,EACA,CAAE,KAAM,EAAK,CACf,CACF"}
|
package/dist/devtools.d.ts
CHANGED
|
@@ -3,11 +3,11 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Import from the dedicated sub-path so it is tree-shaken from production bundles:
|
|
5
5
|
* ```ts
|
|
6
|
-
* import {
|
|
6
|
+
* import { debugForm } from '@vielzeug/forge/devtools';
|
|
7
7
|
* ```
|
|
8
8
|
*/
|
|
9
9
|
import type { Form, Unsubscribe } from './types';
|
|
10
|
-
/** Options for {@link
|
|
10
|
+
/** Options for {@link debugForm}. */
|
|
11
11
|
export type ForgeDevtoolsOptions = {
|
|
12
12
|
/** Label included in every log line, useful when debugging multiple forms at once. Default: `"form"`. */
|
|
13
13
|
label?: string;
|
|
@@ -27,14 +27,14 @@ export type ForgeDevtoolsOptions = {
|
|
|
27
27
|
* @example
|
|
28
28
|
* ```ts
|
|
29
29
|
* import { createForm } from '@vielzeug/forge';
|
|
30
|
-
* import {
|
|
30
|
+
* import { debugForm } from '@vielzeug/forge/devtools';
|
|
31
31
|
*
|
|
32
32
|
* const form = createForm({ defaultValues: { email: '' } });
|
|
33
|
-
* const detach =
|
|
33
|
+
* const detach = debugForm(form, { label: 'signup' });
|
|
34
34
|
* // [forge:devtools:signup] field "email" value: "" → "a@b.com"
|
|
35
35
|
*
|
|
36
36
|
* detach(); // stop logging
|
|
37
37
|
* ```
|
|
38
38
|
*/
|
|
39
|
-
export declare function
|
|
39
|
+
export declare function debugForm<TValues extends Record<string, unknown>>(form: Form<TValues>, options?: ForgeDevtoolsOptions): Unsubscribe;
|
|
40
40
|
//# sourceMappingURL=devtools.d.ts.map
|
package/dist/devtools.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"devtools.d.ts","sourceRoot":"","sources":["../src/devtools.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAyB,IAAI,EAAa,WAAW,EAAE,MAAM,SAAS,CAAC;AAMnF
|
|
1
|
+
{"version":3,"file":"devtools.d.ts","sourceRoot":"","sources":["../src/devtools.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAyB,IAAI,EAAa,WAAW,EAAE,MAAM,SAAS,CAAC;AAMnF,qCAAqC;AACrC,MAAM,MAAM,oBAAoB,GAAG;IACjC,yGAAyG;IACzG,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,SAAS,CAAC,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/D,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,EACnB,OAAO,GAAE,oBAAyB,GACjC,WAAW,CAgEb"}
|
package/dist/devtools.js
CHANGED
package/dist/devtools.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"devtools.js","names":[],"sources":["../src/devtools.ts"],"sourcesContent":["/**\n * @vielzeug/forge — debug utilities for form state visualisation.\n *\n * Import from the dedicated sub-path so it is tree-shaken from production bundles:\n * ```ts\n * import {
|
|
1
|
+
{"version":3,"file":"devtools.js","names":[],"sources":["../src/devtools.ts"],"sourcesContent":["/**\n * @vielzeug/forge — debug utilities for form state visualisation.\n *\n * Import from the dedicated sub-path so it is tree-shaken from production bundles:\n * ```ts\n * import { debugForm } from '@vielzeug/forge/devtools';\n * ```\n */\n\nimport type { FieldState, FlatKeyOf, Form, FormState, Unsubscribe } from './types';\n\nimport { flattenValues, sanitizeForLog } from './_utils';\n\nconst isDev = !(globalThis as { __FORGE_PROD__?: boolean }).__FORGE_PROD__;\n\n/** Options for {@link debugForm}. */\nexport type ForgeDevtoolsOptions = {\n /** Label included in every log line, useful when debugging multiple forms at once. Default: `\"form\"`. */\n label?: string;\n};\n\n/**\n * Attaches a `console.debug`-based observer to `form` that logs a distinct line per observable\n * state transition: per-field value/error/touched/dirty changes, submit start/end (via the\n * `isSubmitting` edge), and `isLoading` edge (async `defaultValues` resolving).\n *\n * **Development only** by default — logging is a no-op when `__FORGE_PROD__` is set (the same\n * convention `_dev.ts` uses internally). Import from the dedicated `/devtools` sub-path so the\n * logging code is tree-shaken from production bundles entirely.\n *\n * Works identically on scoped sub-forms (`form.scope('address')`) — field paths logged are\n * relative to whatever `form` object is passed in, matching that form's own `state` convention.\n *\n * @example\n * ```ts\n * import { createForm } from '@vielzeug/forge';\n * import { debugForm } from '@vielzeug/forge/devtools';\n *\n * const form = createForm({ defaultValues: { email: '' } });\n * const detach = debugForm(form, { label: 'signup' });\n * // [forge:devtools:signup] field \"email\" value: \"\" → \"a@b.com\"\n *\n * detach(); // stop logging\n * ```\n */\nexport function debugForm<TValues extends Record<string, unknown>>(\n form: Form<TValues>,\n options: ForgeDevtoolsOptions = {},\n): Unsubscribe {\n if (!isDev) return () => {};\n\n const prefix = `[forge:devtools:${options.label ?? 'form'}]`;\n const fieldSnapshots = new Map<string, FieldState<unknown>>();\n\n let prevIsSubmitting = form.state.isSubmitting;\n let prevIsLoading = form.state.isLoading;\n\n function knownFieldKeys(state: FormState): Set<string> {\n const keys = new Set<string>(fieldSnapshots.keys());\n\n for (const key of Object.keys(flattenValues(form.values() as Record<string, unknown>))) keys.add(key);\n for (const key of Object.keys(state.errors)) keys.add(key);\n for (const key of state.touchedFields) keys.add(key);\n\n return keys;\n }\n\n function logFieldDiffs(state: FormState): void {\n for (const key of knownFieldKeys(state)) {\n const next = form.field(key as FlatKeyOf<TValues>);\n const prev = fieldSnapshots.get(key);\n\n fieldSnapshots.set(key, next);\n\n // First observation of this field establishes a baseline — logging it would just be\n // noise (every field would \"change\" from undefined on attach).\n if (!prev) continue;\n\n // Security: `key` is a caller-chosen field path (assertSafeKey only rejects reserved\n // segments, not control characters) — sanitize before interpolating into console output\n // to prevent terminal-escape-sequence injection, mirroring the sanitization `warn()`\n // call sites already apply (see `_utils.ts` sanitizeForLog).\n const safeKey = sanitizeForLog(key, 80);\n\n if (prev.value !== next.value) console.debug(`${prefix} field \"${safeKey}\" value:`, prev.value, '→', next.value);\n\n if (prev.error !== next.error) console.debug(`${prefix} field \"${safeKey}\" error:`, prev.error, '→', next.error);\n\n if (prev.touched !== next.touched) console.debug(`${prefix} field \"${safeKey}\" touched:`, next.touched);\n\n if (prev.dirty !== next.dirty) console.debug(`${prefix} field \"${safeKey}\" dirty:`, next.dirty);\n }\n }\n\n return form.subscribe(\n (state) => {\n logFieldDiffs(state);\n\n if (state.isSubmitting !== prevIsSubmitting) {\n prevIsSubmitting = state.isSubmitting;\n console.debug(\n `${prefix} submit ${state.isSubmitting ? 'started' : 'ended'} (submitCount=${state.submitCount})`,\n );\n }\n\n if (state.isLoading !== prevIsLoading) {\n prevIsLoading = state.isLoading;\n console.debug(`${prefix} isLoading:`, state.isLoading);\n }\n },\n { sync: true },\n );\n}\n"],"mappings":";;AAaA,IAAM,IAAQ,CAAE,WAA4C;AAgC5D,SAAgB,EACd,GACA,IAAgC,CAAC,GACpB;CACb,IAAI,CAAC,GAAO,aAAa,CAAC;CAE1B,IAAM,IAAS,mBAAmB,EAAQ,SAAS,OAAO,IACpD,oBAAiB,IAAI,IAAiC,GAExD,IAAmB,EAAK,MAAM,cAC9B,IAAgB,EAAK,MAAM;CAE/B,SAAS,EAAe,GAA+B;EACrD,IAAM,IAAO,IAAI,IAAY,EAAe,KAAK,CAAC;EAElD,KAAK,IAAM,KAAO,OAAO,KAAK,EAAc,EAAK,OAAO,CAA4B,CAAC,GAAG,EAAK,IAAI,CAAG;EACpG,KAAK,IAAM,KAAO,OAAO,KAAK,EAAM,MAAM,GAAG,EAAK,IAAI,CAAG;EACzD,KAAK,IAAM,KAAO,EAAM,eAAe,EAAK,IAAI,CAAG;EAEnD,OAAO;CACT;CAEA,SAAS,EAAc,GAAwB;EAC7C,KAAK,IAAM,KAAO,EAAe,CAAK,GAAG;GACvC,IAAM,IAAO,EAAK,MAAM,CAAyB,GAC3C,IAAO,EAAe,IAAI,CAAG;GAMnC,IAJA,EAAe,IAAI,GAAK,CAAI,GAIxB,CAAC,GAAM;GAMX,IAAM,IAAU,EAAe,GAAK,EAAE;GAQtC,AANI,EAAK,UAAU,EAAK,SAAO,QAAQ,MAAM,GAAG,EAAO,UAAU,EAAQ,WAAW,EAAK,OAAO,KAAK,EAAK,KAAK,GAE3G,EAAK,UAAU,EAAK,SAAO,QAAQ,MAAM,GAAG,EAAO,UAAU,EAAQ,WAAW,EAAK,OAAO,KAAK,EAAK,KAAK,GAE3G,EAAK,YAAY,EAAK,WAAS,QAAQ,MAAM,GAAG,EAAO,UAAU,EAAQ,aAAa,EAAK,OAAO,GAElG,EAAK,UAAU,EAAK,SAAO,QAAQ,MAAM,GAAG,EAAO,UAAU,EAAQ,WAAW,EAAK,KAAK;EAChG;CACF;CAEA,OAAO,EAAK,WACT,MAAU;EAUT,AATA,EAAc,CAAK,GAEf,EAAM,iBAAiB,MACzB,IAAmB,EAAM,cACzB,QAAQ,MACN,GAAG,EAAO,UAAU,EAAM,eAAe,YAAY,QAAQ,gBAAgB,EAAM,YAAY,EACjG,IAGE,EAAM,cAAc,MACtB,IAAgB,EAAM,WACtB,QAAQ,MAAM,GAAG,EAAO,cAAc,EAAM,SAAS;CAEzD,GACA,EAAE,MAAM,GAAK,CACf;AACF"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vielzeug/forge",
|
|
3
|
-
"version": "1.1
|
|
3
|
+
"version": "1.2.1",
|
|
4
4
|
"description": "Type-safe form state — field-level validation, submission, dirty tracking, field arrays, and async default values",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
@@ -43,14 +43,17 @@
|
|
|
43
43
|
"access": "public",
|
|
44
44
|
"registry": "https://registry.npmjs.org/"
|
|
45
45
|
},
|
|
46
|
+
"engines": {
|
|
47
|
+
"node": ">=18"
|
|
48
|
+
},
|
|
46
49
|
"dependencies": {
|
|
47
|
-
"@vielzeug/arsenal": "
|
|
48
|
-
"@vielzeug/ripple": "
|
|
50
|
+
"@vielzeug/arsenal": "1.1.1",
|
|
51
|
+
"@vielzeug/ripple": "1.2.1"
|
|
49
52
|
},
|
|
50
53
|
"devDependencies": {
|
|
51
54
|
"@types/node": "^26.1.0",
|
|
52
|
-
"typescript": "
|
|
55
|
+
"typescript": "^6.0.3",
|
|
53
56
|
"vite": "^8.1.3",
|
|
54
|
-
"vitest": "^4.1.
|
|
57
|
+
"vitest": "^4.1.10"
|
|
55
58
|
}
|
|
56
59
|
}
|