Page Menu
Home
Sealhub
Search
Configure Global Search
Log In
Files
F7188387
fieldset.ts
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
6 KB
Referenced Files
None
Subscribers
None
fieldset.ts
View Options
import
type
Context
from
"../context"
;
import
type
{
ExtractInput
,
ExtractStorage
,
FieldOutput
,
GetInputType
,
RequiredField
,
}
from
"./field"
;
import
type
Field
from
"./field"
;
export
type
FieldNames
<
T
extends
Record
<
string
,
Field
>>
=
keyof
T
&
string
;
export
type
FieldsetOutput
<
T
extends
Record
<
string
,
Field
>>
=
{
[
field
in
keyof
T
]
:
FieldOutput
<
T
[
field
]
>
|
null
;
};
export
type
FieldsetInput
<
T
extends
Record
<
string
,
Field
>>
=
|
{
[
field
in
keyof
T
&
string
as
T
[
field
]
extends
RequiredField
<
any
>
?
field
:
never
]
:
GetInputType
<
T
[
field
]
>
;
}
&
{
[
field
in
keyof
T
&
string
as
T
[
field
]
extends
RequiredField
<
any
>
?
never
:
field
]
?:
GetInputType
<
T
[
field
]
>
;
};
export
type
FieldsetEncoded
<
T
extends
Record
<
string
,
Field
>>
=
{
[
field
in
keyof
T
]
:
ExtractStorage
<
T
[
field
]
>
;
};
// a quick test for the types, shoud know which fields can be undefined
// const f = {
// a: new FieldTypes.Text().setRequired(true),
// b: new FieldTypes.Text(),
// };
// type fi = FieldsetInput<typeof f>;
export
class
Fieldset
<
Fields
extends
Record
<
string
,
Field
>>
{
changed_fields
:
Set
<
keyof
Fields
>
=
new
Set
();
is_decoded
=
false
;
is_encoded
=
false
;
blessings
:
Partial
<
Record
<
keyof
Fields
,
symbol
|
null
>>
=
{};
constructor
(
public
fields
:
Fields
,
public
raw_input
:
Partial
<
FieldsetInput
<
Fields
>>
=
{},
public
decoded
:
Partial
<
FieldsetOutput
<
Fields
>>
=
{},
public
encoded
:
Partial
<
FieldsetEncoded
<
Fields
>>
=
{}
)
{
for
(
const
field_name
in
raw_input
)
{
if
(
!
encoded
[
field_name
as
keyof
Fields
])
{
this
.
changed_fields
.
add
(
field_name
as
keyof
Fields
);
}
}
}
getRequiredFields
()
:
Field
[]
{
return
Object
.
values
(
this
.
fields
).
filter
((
f
)
=>
f
.
required
);
}
set
<
FieldName
extends
keyof
FieldsetInput
<
Fields
>
&
string
>
(
field_name
:
FieldName
,
field_value
:
FieldsetInput
<
Fields
>
[
FieldName
],
blessing_symbol
:
symbol
|
null
=
null
// those symbols can be used as a proof that a value came from e.g. an internal callback, and not from user input
)
:
this
{
this
.
raw_input
[
field_name
]
=
field_value
;
this
.
is_decoded
=
false
;
this
.
is_encoded
=
false
;
this
.
changed_fields
.
add
(
field_name
);
this
.
blessings
[
field_name
]
=
blessing_symbol
;
return
this
;
}
clearChangedFields
()
{
this
.
changed_fields
.
clear
();
}
getDecoded
<
FieldName
extends
keyof
Fields
>
(
field_name
:
FieldName
)
{
if
(
!
this
.
is_decoded
)
{
throw
new
Error
(
"Decode first!"
);
}
return
this
.
decoded
[
field_name
as
keyof
typeof
this
.
decoded
];
}
getInput
<
FieldName
extends
keyof
FieldsetInput
<
Fields
>>
(
field_name
:
FieldName
)
{
return
this
.
raw_input
[
field_name
];
}
getEncoded
<
FieldName
extends
keyof
Fields
>
(
field_name
:
FieldName
)
{
return
this
.
encoded
[
field_name
];
}
/** Returns encoded values for every field */
async
encode
(
context
:
Context
,
original_body
:
FieldsetInput
<
Fields
>
=
{}
as
FieldsetInput
<
Fields
>
,
only_changed
=
false
)
:
Promise
<
Partial
<
FieldsetOutput
<
Fields
>>>
{
context
.
app
.
Logger
.
debug3
(
"ITEM BODY"
,
"encode"
,
this
.
changed_fields
.
values
()
);
const
new_encoded
:
Partial
<
FieldsetOutput
<
Fields
>>
=
{};
const
promises
=
[];
for
(
const
field_name
of
this
.
changed_fields
.
values
())
{
const
to_encode
=
this
.
raw_input
[
field_name
as
keyof
FieldsetInput
<
Fields
>
];
context
.
app
.
Logger
.
debug3
(
"ITEM BODY"
,
"encoding value"
,
{
[
field_name
]
:
this
.
raw_input
[
field_name
as
keyof
FieldsetInput
<
Fields
>
],
is_the_value_empty
:
to_encode
===
undefined
||
to_encode
===
null
,
});
if
(
!
this
.
fields
[
field_name
as
string
])
{
// field does not exist in this collection
continue
;
}
if
(
to_encode
===
undefined
)
{
new_encoded
[
field_name
as
keyof
typeof
new_encoded
]
=
null
;
continue
;
}
promises
.
push
(
this
.
fields
[
field_name
as
string
]
.
encode
(
context
,
to_encode
,
original_body
[
field_name
as
keyof
FieldsetInput
<
Fields
>
]
)
.
then
((
value
)
=>
{
new_encoded
[
field_name
as
keyof
typeof
new_encoded
]
=
value
;
})
);
}
await
Promise
.
all
(
promises
);
this
.
encoded
=
{
...
this
.
encoded
,
...
new_encoded
};
this
.
is_encoded
=
true
;
context
.
app
.
Logger
.
debug2
(
"ITEM BODY"
,
"encode result"
,
this
.
encoded
);
return
only_changed
?
new_encoded
:
this
.
encoded
;
}
async
decode
(
context
:
Context
,
format
:
{
[
field_name
:
string
]
:
any
}
=
{}
)
:
Promise
<
Fieldset
<
Fields
>>
{
if
(
this
.
is_decoded
)
return
this
;
context
.
app
.
Logger
.
debug3
(
"ITEM BODY"
,
"Decoding item"
,
{
format
,
body
:
this
.
encoded
,
});
const
promises
:
Promise
<
any
>
[]
=
[];
for
(
const
[
field_name
,
encoded_value
]
of
Object
.
entries
(
this
.
encoded
))
{
if
(
!
this
.
fields
?
.[
field_name
])
{
continue
;
}
const
encoded
=
this
.
encoded
;
promises
.
push
(
this
.
fields
?
.[
field_name
]
.
decode
(
context
,
encoded
[
field_name
],
null
,
format
?
.[
field_name
]
)
.
then
((
decoded_value
)
=>
{
this
.
decoded
=
{
...
this
.
decoded
,
[
field_name
]
:
decoded_value
,
};
context
.
app
.
Logger
.
debug3
(
"ITEM BODY"
,
"Decoded value"
,
{
[
field_name
]
:
decoded_value
,
}
);
})
);
}
await
Promise
.
all
(
promises
);
this
.
is_decoded
=
true
;
return
this
;
}
copy
()
:
Fieldset
<
Fields
>
{
return
new
Fieldset
<
Fields
>
(
this
.
fields
,
{
...
this
.
raw_input
},
{
...
this
.
decoded
},
{
...
this
.
encoded
}
);
}
async
validate
(
context
:
Context
,
original_body
:
Fieldset
<
Fields
>
,
replace_mode
:
boolean
//if true, meaning that if a field has no value, it should be deleted
)
:
Promise
<
{
valid
:
boolean
;
errors
:
{
[
f
in
keyof
Fields
]
?:
{
message
:
string
}
};
}
>
{
const
promises
=
[];
const
errors
:
{
[
f
in
keyof
Fields
]
?:
{
message
:
string
}
}
=
{};
let
valid
=
true
;
const
fields_to_check
=
new
Set
(
this
.
changed_fields
.
values
());
if
(
replace_mode
)
{
for
(
const
field
of
this
.
getRequiredFields
())
{
fields_to_check
.
add
(
field
.
name
as
keyof
Fields
);
}
}
for
(
const
field_name
of
fields_to_check
)
{
if
(
!
this
.
fields
[
field_name
as
string
])
{
// field does not exist
continue
;
}
promises
.
push
(
this
.
fields
[
field_name
as
string
]
.
checkValue
(
context
,
this
.
raw_input
[
field_name
as
keyof
FieldsetInput
<
Fields
>
],
original_body
.
encoded
[
field_name
as
keyof
Fields
],
this
.
blessings
[
field_name
]
||
null
)
.
then
(
async
(
result
)
=>
{
if
(
!
result
.
valid
)
{
valid
=
false
;
errors
[
field_name
]
=
{
message
:
result
.
reason
as
string
,
};
}
})
);
}
await
Promise
.
all
(
promises
);
return
{
valid
,
errors
};
}
getBlessing
<
FieldName
extends
keyof
Fields
>
(
field_name
:
FieldName
)
:
symbol
|
null
{
return
this
.
blessings
[
field_name
]
||
null
;
}
setMultiple
(
values
:
Partial
<
FieldsetInput
<
Fields
>>
)
:
this
{
for
(
const
[
field_name
,
value
]
of
Object
.
entries
(
values
))
{
this
.
set
(
field_name
as
any
,
value
as
any
);
}
return
this
;
}
}
File Metadata
Details
Attached
Mime Type
text/x-java
Expires
Tue, Jul 8, 08:10 (1 d, 12 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
799960
Default Alt Text
fieldset.ts (6 KB)
Attached To
Mode
rS Sealious
Attached
Detach File
Event Timeline
Log In to Comment