UI: Add Typescript for PKI engine (#17927)

This commit is contained in:
Chelsea Shaw 2022-11-15 11:39:46 -06:00 committed by GitHub
parent de70878e16
commit 0fb4e422be
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 1117 additions and 496 deletions

3
changelog/17927.txt Normal file
View File

@ -0,0 +1,3 @@
```release-note:improvement
ui: Enable typescript for future development
```

14
ui/app/config/environment.d.ts vendored Normal file
View File

@ -0,0 +1,14 @@
/**
* Type declarations for
* import config from 'my-app/config/environment'
*/
declare const config: {
environment: string;
modulePrefix: string;
podModulePrefix: string;
locationType: 'history' | 'hash' | 'none' | 'auto';
rootURL: string;
APP: Record<string, unknown>;
};
export default config;

View File

@ -10,6 +10,14 @@ const validations = {
@withModelValidations(validations)
export default class PkiRoleModel extends Model {
get useOpenAPI() {
// must be a getter so it can be accessed in path-help.js
return true;
}
getHelpUrl(backend) {
return `/v1/${backend}/roles/example?help=1`;
}
@attr('string', { readOnly: true }) backend;
/* Overriding OpenApi default options */
@ -219,6 +227,20 @@ export default class PkiRoleModel extends Model {
})
ou;
@attr('array', {
defaultValue() {
return ['DigitalSignature', 'KeyAgreement', 'KeyEncipherment'];
},
})
keyUsage;
@attr('array', {
defaultValue() {
return [];
},
})
extKeyUsage;
@attr({ hideFormSection: true }) organization;
@attr({ hideFormSection: true }) country;
@attr({ hideFormSection: true }) locality;
@ -227,14 +249,7 @@ export default class PkiRoleModel extends Model {
@attr({ hideFormSection: true }) postalCode;
/* End of overriding Additional subject field options */
// must be a getter so it can be added to the prototype needed in the pathHelp service on the line here: if (newModel.merged || modelProto.useOpenAPI !== true) {
get useOpenAPI() {
return true;
}
getHelpUrl(backend) {
return `/v1/${backend}/roles/example?help=1`;
}
/* CAPABILITIES */
@lazyCapabilities(apiPath`${'backend'}/roles/${'id'}`, 'backend', 'id') updatePath;
get canDelete() {
return this.updatePath.get('canCreate');

View File

@ -0,0 +1,23 @@
<div data-test-checkbox-grid ...attributes>
<FormFieldLabel for={{@name}} @label={{@label}} @subText={{@subText}} />
<div class="is-grid is-grid-3-columns is-medium-height">
{{#each this.checkboxes as |checkbox|}}
<div class="field">
<div class="b-checkbox">
<Input
@type="checkbox"
id={{checkbox.key}}
name={{checkbox.key}}
class="styled"
@checked={{checkbox.value}}
{{on "change" this.checkboxChange}}
data-test-checkbox={{checkbox.key}}
/>
<label for={{checkbox.key}} class="is-label">
{{checkbox.label}}
</label>
</div>
</div>
{{/each}}
</div>
</div>

View File

@ -0,0 +1,55 @@
import Component from '@glimmer/component';
import { action } from '@ember/object';
import { HTMLElementEvent } from 'forms';
interface CheckboxGridArgs {
name: string;
label: string;
subText?: string;
fields: Field[];
value: string[] | undefined;
onChange: (name: string, value: string[]) => void;
}
interface Field {
key: string;
label: string;
}
/**
* @module CheckboxGrid
* CheckboxGrid components are used to allow users to select any
* number of predetermined options, aligned in a 3-column grid.
*
* @example
* ```js
* <CheckboxGrid
* @name="modelKey"
* @label="Model Attribute Label"
* @fields={{options}}
* @value={{['Hello', 'Yes']}}
* />
* ```
*/
export default class CheckboxGrid extends Component<CheckboxGridArgs> {
get checkboxes() {
const list = this.args.value || [];
return this.args.fields.map((field) => ({
...field,
value: list.includes(field.key),
}));
}
@action checkboxChange(event: HTMLElementEvent<HTMLInputElement>) {
const list = this.args.value || [];
const checkboxName = event.target.id;
const checkboxVal = event.target.checked;
const idx = list.indexOf(checkboxName);
if (checkboxVal === true && idx < 0) {
list.push(checkboxName);
} else if (checkboxVal === false && idx >= 0) {
list.splice(idx, 1);
}
this.args.onChange(this.args.name, list);
}
}

View File

@ -0,0 +1 @@
export { default } from 'core/components/checkbox-grid';

View File

@ -14,6 +14,7 @@
"ember-cli-clipboard": "*",
"ember-cli-htmlbars": "*",
"ember-cli-string-helpers": "*",
"ember-cli-typescript": "*",
"ember-composable-helpers": "*",
"ember-concurrency": "*",
"ember-maybe-in-element": "*",

View File

@ -9,58 +9,25 @@
/>
{{#if (get @model prop)}}
<div class="box is-tall is-marginless" data-test-surrounding-div={{@group}}>
<FormFieldLabel
for="keyUsageLabel"
<CheckboxGrid
@name="keyUsage"
@label="Key usage"
@subText="Specifies the default key usage constraint on the issued certificate. To specify no default key_usage constraints, uncheck every item in this list."
@fields={{this.keyUsageFields}}
@value={{@model.keyUsage}}
@onChange={{this.checkboxChange}}
data-test-key-usage-key-usage-checkboxes
/>
<CheckboxGrid
@name="extKeyUsage"
@label="Extended key usage"
@subText="Specifies the default key usage constraint on the issued certificate. To specify no default ext_key_usage constraints, uncheck every item in this list."
@fields={{this.extKeyUsageFields}}
@value={{@model.extKeyUsage}}
@onChange={{this.checkboxChange}}
class="has-top-margin-s"
data-test-key-usage-ext-key-usage-checkboxes
/>
<div class="is-grid is-grid-3-columns is-medium-height">
{{! KEY USAGE SECTION }}
{{#each-in this.keyUsageFields as |name attr|}}
<div class="field">
<div class="b-checkbox">
<input
type="checkbox"
id={{name}}
class="styled"
checked={{attr.value}}
onchange={{fn this.checkboxChange "keyUsage"}}
data-test-input={{name}}
/>
<label for={{name}} class="is-label">
{{attr.label}}
</label>
</div>
</div>
{{/each-in}}
</div>
<div class="has-top-margin-s">
<FormFieldLabel
for="ExtKeyUsageLabel"
@label="Extended key usage"
@subText="Specifies the default key usage constraint on the issued certificate. To specify no default ext_key_usage constraints, uncheck every item in this list."
/>
</div>
{{! EXT KEY USAGE SECTION }}
<div class="is-grid is-grid-3-columns is-medium-height">
{{#each-in this.extKeyUsageFields as |name attr|}}
<div class="field">
<div class="b-checkbox">
<input
type="checkbox"
id={{name}}
class="styled"
checked={{attr.value}}
onchange={{fn this.checkboxChange "extKeyUsage"}}
data-test-input={{name}}
/>
<label for={{name}} class="is-label">
{{attr.label}}
</label>
</div>
</div>
{{/each-in}}
</div>
<div class="has-top-margin-xxl">
<StringList
data-test-input="extKeyUsageOids"

View File

@ -1,95 +0,0 @@
import Component from '@glimmer/component';
import { action } from '@ember/object';
/**
* @module KeyUsage
* KeyUsage components are used to build out the toggle options for PKI's role create/update key_usage, ext_key_usage and ext_key_usage_oids model params.
* Instead of having the user search on the following goLang pages for these options we present them in checkbox form and manually add them to the params as an array of strings.
* key_usage options: https://pkg.go.dev/crypto/x509#KeyUsage
* ext_key_usage options (not all are include on purpose): https://pkg.go.dev/crypto/x509#ExtKeyUsage
* @example
* ```js
* <KeyUsage @model={@model} @group={group}/>
* ```
* @param {class} model - The pki/role model.
* @param {string} group - The name of the group created in the model. In this case, it's the "Key usage" group.
*/
const KEY_USAGE_FIELDS = {
DigitalSignature: {
label: 'Digital Signature',
value: true,
},
ContentCommitment: { label: 'Content Commitment' },
CrlSign: { label: 'CRL Sign' },
KeyAgreement: {
label: 'Key Agreement',
value: true,
},
DataEncipherment: { label: 'Data Encipherment' },
EncipherOnly: { label: 'Encipher Only' },
KeyEncipherment: {
label: 'Key Encipherment',
value: true,
},
CertSign: { label: 'Cert Sign' },
DecipherOnly: { label: 'Decipher Only' },
};
const EXT_KEY_USAGE_FIELDS = {
Any: { label: 'Any' },
EmailProtection: { label: 'Email Protection' },
TimeStamping: { label: 'Time Stamping' },
ServerAuth: { label: 'Server Auth' },
IpsecEndSystem: { label: 'IPSEC End System' },
OcspSigning: { label: 'OCSP Signing' },
ClientAuth: { label: 'Client Auth' },
IpsecTunnel: { label: 'IPSEC Tunnel' },
IpsecUser: { label: 'IPSEC User' },
CodeSigning: { label: 'Code Signing' },
};
export default class KeyUsage extends Component {
constructor() {
super(...arguments);
this.keyUsageFields = {};
this.extKeyUsageFields = {};
Object.assign(this.keyUsageFields, KEY_USAGE_FIELDS);
Object.assign(this.extKeyUsageFields, EXT_KEY_USAGE_FIELDS);
// set default of key_usage to the three params that are true by default.
this.args.model.set('keyUsage', ['DigitalSignature', 'KeyAgreement', 'KeyEncipherment']);
}
@action onStringListChange(value) {
this.args.model.set('extKeyUsageOids', value);
}
_amendList(checkboxName, value, type) {
const keyUsageList = this.args.model.keyUsage;
const extKeyUsageList = this.args.model.extKeyUsage;
/* Process:
1. We first check if the checkbox change is coming from the checkbox options of key_usage or ext_key_usage.
// Param key_usage || ext_key_usage accept a comma separated string and an array of strings. E.g. "DigitalSignature,KeyAgreement,KeyEncipherment" || [“DigitalSignature”,“KeyAgreement”,“KeyEncipherment”]
2. Then we convert the string to an array if it's not already an array (e.g. it's already been converted). This makes it easier to add or remove items.
3. Then if the value of checkbox is "true" we add it to the arrayList, otherwise remove it.
*/
if (type === 'keyUsage') {
const keyUsageListArray = Array.isArray(keyUsageList) ? keyUsageList : keyUsageList.split(',');
return value ? keyUsageListArray.addObject(checkboxName) : keyUsageListArray.removeObject(checkboxName);
} else {
// because there is no default on init for ext_key_usage property (set normally by OpenAPI) we define it as an empty array if it is undefined.
const extKeyUsageListArray = !extKeyUsageList ? [] : extKeyUsageList;
return value
? extKeyUsageListArray.addObject(checkboxName)
: extKeyUsageListArray.removeObject(checkboxName);
}
}
@action checkboxChange(type) {
const checkboxName = event.target.id;
const value = event.target['checked'];
this.args.model.set(type, this._amendList(checkboxName, value, type));
}
}

View File

@ -0,0 +1,82 @@
import Component from '@glimmer/component';
import { action } from '@ember/object';
/**
* @module PkiKeyUsage
* PkiKeyUsage components are used to build out the toggle options for PKI's role create/update key_usage, ext_key_usage and ext_key_usage_oids model params.
* Instead of having the user search on the following goLang pages for these options we present them in checkbox form and manually add them to the params as an array of strings.
* key_usage options: https://pkg.go.dev/crypto/x509#KeyUsage
* ext_key_usage options (not all are include on purpose): https://pkg.go.dev/crypto/x509#ExtKeyUsage
* @example
* ```js
* <PkiKeyUsage @model={@model} @group={group}/>
* ```
* @param {class} model - The pki/pki-role-engine model.
* @param {string} group - The name of the group created in the model. In this case, it's the "Key usage" group.
*/
interface Field {
key: string;
label: string;
}
const KEY_USAGE_FIELDS: Field[] = [
{ key: 'DigitalSignature', label: 'Digital Signature' },
{ key: 'ContentCommitment', label: 'Content Commitment' },
{ key: 'CrlSign', label: 'CRL Sign' },
{ key: 'KeyAgreement', label: 'Key Agreement' },
{ key: 'DataEncipherment', label: 'Data Encipherment' },
{ key: 'EncipherOnly', label: 'Encipher Only' },
{ key: 'KeyEncipherment', label: 'Key Encipherment' },
{ key: 'CertSign', label: 'Cert Sign' },
{ key: 'DecipherOnly', label: 'Decipher Only' },
];
const EXT_KEY_USAGE_FIELDS: Field[] = [
{ key: 'Any', label: 'Any' },
{ key: 'EmailProtection', label: 'Email Protection' },
{ key: 'TimeStamping', label: 'Time Stamping' },
{ key: 'ServerAuth', label: 'Server Auth' },
{ key: 'IpsecEndSystem', label: 'IPSEC End System' },
{ key: 'OcspSigning', label: 'OCSP Signing' },
{ key: 'ClientAuth', label: 'Client Auth' },
{ key: 'IpsecTunnel', label: 'IPSEC Tunnel' },
{ key: 'IpsecUser', label: 'IPSEC User' },
{ key: 'CodeSigning', label: 'Code Signing' },
];
interface PkiKeyUsageArgs {
group: string;
model: {
keyUsage: string[];
extKeyUsageOids: string[];
extKeyUsage: string[];
};
}
export default class PkiKeyUsage extends Component<PkiKeyUsageArgs> {
keyUsageFields = KEY_USAGE_FIELDS;
extKeyUsageFields = EXT_KEY_USAGE_FIELDS;
@action onStringListChange(value: string[]) {
this.args.model.extKeyUsageOids = value;
}
_amendList(checkboxName: string, value: boolean, type: string): string[] {
const list = type === 'keyUsage' ? this.args.model.keyUsage : this.args.model.extKeyUsage;
const idx = list.indexOf(checkboxName);
if (value === true && idx < 0) {
list.push(checkboxName);
} else if (value === false && idx >= 0) {
list.splice(idx, 1);
}
return list;
}
@action checkboxChange(name: string, value: string[]) {
// Make sure we can set this value type to this model key
if (name === 'keyUsage' || name === 'extKeyUsage') {
this.args.model[name] = value;
}
}
}

View File

@ -5,8 +5,9 @@
"ember-engine"
],
"dependencies": {
"ember-cli-babel": "*",
"ember-cli-htmlbars": "*",
"ember-cli-babel": "*"
"ember-cli-typescript": "*"
},
"ember-addon": {
"paths": [

View File

@ -62,6 +62,35 @@
"@hashicorp/ember-flight-icons": "2.0.3",
"@hashicorp/structure-icons": "^1.3.0",
"@icholy/duration": "^5.1.0",
"@tsconfig/ember": "^1.0.1",
"@types/ember": "^4.0.2",
"@types/ember-data": "^4.4.6",
"@types/ember-data__adapter": "^4.0.1",
"@types/ember-data__model": "^4.0.0",
"@types/ember-data__serializer": "^4.0.1",
"@types/ember-data__store": "^4.0.2",
"@types/ember-qunit": "^5.0.2",
"@types/ember-resolver": "^5.0.13",
"@types/ember__application": "^4.0.4",
"@types/ember__array": "^4.0.3",
"@types/ember__component": "^4.0.11",
"@types/ember__controller": "^4.0.3",
"@types/ember__debug": "^4.0.3",
"@types/ember__destroyable": "^4.0.1",
"@types/ember__engine": "^4.0.4",
"@types/ember__error": "^4.0.1",
"@types/ember__object": "^4.0.5",
"@types/ember__polyfills": "^4.0.1",
"@types/ember__routing": "^4.0.12",
"@types/ember__runloop": "^4.0.2",
"@types/ember__service": "^4.0.1",
"@types/ember__string": "^3.0.10",
"@types/ember__template": "^4.0.1",
"@types/ember__test": "^4.0.1",
"@types/ember__test-helpers": "^2.8.2",
"@types/ember__utils": "^4.0.2",
"@types/qunit": "^2.19.3",
"@types/rsvp": "^4.0.4",
"asn1js": "^2.2.0",
"autosize": "^4.0.0",
"babel-eslint": "^10.1.0",
@ -103,6 +132,7 @@
"ember-cli-sri": "meirish/ember-cli-sri#rooturl",
"ember-cli-string-helpers": "6.1.0",
"ember-cli-terser": "^4.0.2",
"ember-cli-typescript": "^5.2.1",
"ember-composable-helpers": "5.0.0",
"ember-concurrency": "2.3.4",
"ember-copy": "2.0.1",
@ -152,7 +182,7 @@
"pkijs": "^2.2.2",
"pretender": "^3.4.3",
"prettier": "2.6.2",
"prettier-eslint-cli": "^5.0.0",
"prettier-eslint-cli": "^7.1.0",
"pvutils": "^1.0.17",
"qunit": "^2.19.1",
"qunit-dom": "^2.0.0",
@ -163,6 +193,7 @@
"string.prototype.startswith": "^0.2.0",
"swagger-ui-dist": "^3.36.2",
"text-encoder-lite": "2.0.0",
"typescript": "^4.8.4",
"walk-sync": "^2.0.2",
"webpack": "5.73.0",
"xstate": "^3.3.3"

View File

@ -17,11 +17,11 @@ export const SELECTORS = {
signatureBits: '[data-test-input="signatureBits"]',
keyUsage: '[data-test-toggle-group="Key usage"]',
extKeyUsageOids: '[data-test-input="extKeyUsageOids"]',
digitalSignature: '[data-test-input="DigitalSignature"]',
keyAgreement: '[data-test-input="KeyAgreement"]',
keyEncipherment: '[data-test-input="KeyEncipherment"]',
any: '[data-test-input="Any"]',
serverAuth: '[data-test-input="ServerAuth"]',
digitalSignature: '[data-test-checkbox="DigitalSignature"]',
keyAgreement: '[data-test-checkbox="KeyAgreement"]',
keyEncipherment: '[data-test-checkbox="KeyEncipherment"]',
any: '[data-test-checkbox="Any"]',
serverAuth: '[data-test-checkbox="ServerAuth"]',
policyIdentifiers: '[data-test-toggle-group="Policy identifiers"]',
san: '[data-test-toggle-group="Subject Alternative Name (SAN) Options"]',
additionalSubjectFields: '[data-test-toggle-group="Additional subject fields"]',

View File

@ -0,0 +1,54 @@
import { module, test } from 'qunit';
import { setupRenderingTest } from 'ember-qunit';
import { render, click } from '@ember/test-helpers';
import { hbs } from 'ember-cli-htmlbars';
import Sinon from 'sinon';
module('Integration | Component | checkbox-grid', function (hooks) {
setupRenderingTest(hooks);
hooks.beforeEach(function () {
this.name = 'fooBar';
this.label = 'Foo bar';
this.fields = [
{ key: 'abc', label: 'All Bears Cry' },
{ key: 'def', label: 'Dark Eel Feelings' },
];
this.onChange = Sinon.spy();
});
test('it renders with minimum inputs', async function (assert) {
const changeSpy = Sinon.spy();
this.set('onChange', changeSpy);
await render(
hbs`<CheckboxGrid @name={{this.name}} @label={{this.label}} @fields={{this.fields}} @onChange={{this.onChange}} />`
);
assert.dom('[data-test-checkbox]').exists({ count: 2 }, 'One checkbox is rendered for each field');
assert.dom('[data-test-checkbox]').isNotChecked('no fields are checked by default');
await click('[data-test-checkbox="abc"]');
assert.ok(changeSpy.calledOnceWithExactly('fooBar', ['abc']));
});
test('it renders with values set', async function (assert) {
const changeSpy = Sinon.spy();
this.set('onChange', changeSpy);
this.set('currentValue', ['abc']);
await render(
hbs`<CheckboxGrid @name={{this.name}} @label={{this.label}} @fields={{this.fields}} @onChange={{this.onChange}} @value={{this.currentValue}} />`
);
assert.dom('[data-test-checkbox]').exists({ count: 2 }, 'One checkbox is rendered for each field');
assert.dom('[data-test-checkbox="abc"]').isChecked('abc field is checked on load');
assert.dom('[data-test-checkbox="def"]').isNotChecked('def field is unchecked on load');
await click('[data-test-checkbox="abc"]');
assert.ok(changeSpy.calledOnceWithExactly('fooBar', []), 'Sends correct payload when unchecking');
await click('[data-test-checkbox="def"]');
await click('[data-test-checkbox="abc"]');
assert.ok(
changeSpy.calledWithExactly('fooBar', ['def', 'abc']),
'sends correct payload with multiple checked'
);
});
});

View File

@ -46,7 +46,7 @@ module('Integration | Component | pki/key-usage', function (hooks) {
});
test('it should set the model properties of key_usage and ext_key_usage based on the checkbox selections', async function (assert) {
assert.expect(4);
assert.expect(2);
await render(
hbs`
<div class="has-top-margin-xxl">
@ -58,17 +58,6 @@ module('Integration | Component | pki/key-usage', function (hooks) {
`,
{ owner: this.engine }
);
// See PKI API docs https://developer.hashicorp.com/vault/api-docs/secret/pki#key_usage
assert.deepEqual(
this.model.keyUsage,
['DigitalSignature', 'KeyAgreement', 'KeyEncipherment'],
'sets the default values for key_usage on the model.'
);
assert.strictEqual(
this.model.extKeyUsage,
undefined,
'sets no default value set for ext_key_usage on load.'
);
await click(SELECTORS.keyUsage);
await click(SELECTORS.digitalSignature);

70
ui/tsconfig.json Normal file
View File

@ -0,0 +1,70 @@
{
"extends": "@tsconfig/ember/tsconfig.json",
"compilerOptions": {
// The combination of `baseUrl` with `paths` allows Ember's classic package
// layout, which is not resolvable with the Node resolution algorithm, to
// work with TypeScript.
"baseUrl": ".",
"paths": {
"vault/tests/*": ["tests/*"],
"vault/mirage/*": ["mirage/*"],
"vault/*": [
"app/*",
"lib/core/app/*",
"lib/css/app/*",
"lib/kmip/app/*",
"lib/open-api-explorer/app/*",
"lib/pki/app/*",
"lib/replication/app/*",
"lib/service-worker-authenticated-download/app/*"
],
"core": ["lib/core/addon"],
"core/*": ["lib/core/addon/*"],
"core/test-support": ["lib/core/addon-test-support"],
"core/test-support/*": ["lib/core/addon-test-support/*"],
"css": ["lib/css/addon"],
"css/*": ["lib/css/addon/*"],
"css/test-support": ["lib/css/addon-test-support"],
"css/test-support/*": ["lib/css/addon-test-support/*"],
"kmip": ["lib/kmip/addon"],
"kmip/*": ["lib/kmip/addon/*"],
"kmip/test-support": ["lib/kmip/addon-test-support"],
"kmip/test-support/*": ["lib/kmip/addon-test-support/*"],
"open-api-explorer": ["lib/open-api-explorer/addon"],
"open-api-explorer/*": ["lib/open-api-explorer/addon/*"],
"open-api-explorer/test-support": ["lib/open-api-explorer/addon-test-support"],
"open-api-explorer/test-support/*": ["lib/open-api-explorer/addon-test-support/*"],
"pki": ["lib/pki/addon"],
"pki/*": ["lib/pki/addon/*"],
"pki/test-support": ["lib/pki/addon-test-support"],
"pki/test-support/*": ["lib/pki/addon-test-support/*"],
"replication": ["lib/replication/addon"],
"replication/*": ["lib/replication/addon/*"],
"replication/test-support": ["lib/replication/addon-test-support"],
"replication/test-support/*": ["lib/replication/addon-test-support/*"],
"service-worker-authenticated-download": ["lib/service-worker-authenticated-download/addon"],
"service-worker-authenticated-download/*": ["lib/service-worker-authenticated-download/addon/*"],
"service-worker-authenticated-download/test-support": [
"lib/service-worker-authenticated-download/addon-test-support"
],
"service-worker-authenticated-download/test-support/*": [
"lib/service-worker-authenticated-download/addon-test-support/*"
],
"*": ["types/*"]
}
},
"include": [
"app/**/*",
"tests/**/*",
"types/**/*",
"lib/core/**/*",
"lib/css/**/*",
"lib/kmip/**/*",
"lib/open-api-explorer/**/*",
"lib/pki/**/*",
"lib/replication/**/*",
"lib/service-worker-authenticated-download/**/*",
"mirage/**/*"
],
"exclude": ["node_modules"]
}

View File

@ -0,0 +1,6 @@
/**
* Catch-all for ember-data.
*/
export default interface ModelRegistry {
[key: string]: any;
}

4
ui/types/forms.ts Normal file
View File

@ -0,0 +1,4 @@
export type HTMLElementEvent<T extends HTMLElement> = Event & {
target: T;
currentTarget: T;
};

7
ui/types/global.d.ts vendored Normal file
View File

@ -0,0 +1,7 @@
// Types for compiled templates
declare module 'vault/templates/*' {
import { TemplateFactory } from 'ember-cli-htmlbars';
const tmpl: TemplateFactory;
export default tmpl;
}

11
ui/types/vault/index.d.ts vendored Normal file
View File

@ -0,0 +1,11 @@
import Ember from 'ember';
declare global {
// Prevents ESLint from "fixing" this via its auto-fix to turn it into a type
// alias (e.g. after running any Ember CLI generator)
// eslint-disable-next-line @typescript-eslint/no-empty-interface
interface Array<T> extends Ember.ArrayPrototypeExtensions<T> {}
// interface Function extends Ember.FunctionPrototypeExtensions {}
}
export {};

File diff suppressed because it is too large Load Diff