@protontech/autofill 0.0.35059265 → 0.0.35481761
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/features/feature.d.ts +13 -2
- package/features/feature.js +37 -15
- package/features/feature.spec.js +56 -7
- package/features/v1/abstract.field.d.ts +8377 -406
- package/features/v1/abstract.field.js +24 -24
- package/features/v1/abstract.form.d.ts +3428 -14
- package/features/v1/abstract.form.js +197 -197
- package/features/v1/field.email.d.ts +3421 -0
- package/features/v1/field.email.js +7 -7
- package/features/v1/field.otp.d.ts +71178 -0
- package/features/v1/field.otp.js +42 -42
- package/features/v1/field.password.d.ts +99166 -0
- package/features/v1/field.password.js +42 -42
- package/features/v1/field.username-hidden.d.ts +908 -0
- package/features/v1/field.username-hidden.js +7 -7
- package/features/v1/field.username.d.ts +42382 -0
- package/features/v1/field.username.js +11 -11
- package/features/v1/fields.sorted.gen.d.ts +1441 -0
- package/features/v1/fields.sorted.gen.js +344 -0
- package/features/v1/forms.sorted.gen.d.ts +734 -0
- package/features/v1/forms.sorted.gen.js +334 -0
- package/features/v1/index.d.ts +5305 -12
- package/features/v1/index.js +7 -11
- package/features/v1/index.spec.js +34 -20
- package/models/perceptron/params/login-model.json +91 -91
- package/models/perceptron/params/new-password-model.json +21 -21
- package/models/perceptron/params/otp-model.json +29 -29
- package/models/perceptron/params/password-change-model.json +72 -72
- package/models/perceptron/params/password-model.json +23 -23
- package/models/perceptron/params/recovery-model.json +81 -81
- package/models/perceptron/params/register-model.json +99 -99
- package/models/perceptron/params/username-hidden-model.json +10 -10
- package/models/perceptron/params/username-model.json +7 -7
- package/models/random_forest/params/otp-model.json +306 -366
- package/package.json +3 -3
- package/rules/v1/index.js +2 -2
- package/scripts/gen-sorted-features.js +125 -29
- package/types/index.d.ts +2 -1
- package/utils/fathom.d.ts +1 -2
- package/utils/fathom.js +2 -2
- package/features/registry.d.ts +0 -3
- package/features/registry.js +0 -9
- package/features/sorted.gen.d.ts +0 -14
- package/features/sorted.gen.js +0 -22
package/features/feature.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import type { AbstractFeature, AbstractFeatures, Feature, FeatureComputer, InferFeatureName, InferFeatureReturnType, InferParentComputeType } from "@protontech/autofill/types";
|
|
2
2
|
import type { Fnode } from "@protontech/fathom";
|
|
3
|
-
export declare const feature: <N extends string, R, P extends AbstractFeatures>(name: N, parents: P, compute: (parents: InferParentComputeType<P>, fnode: Fnode) => R
|
|
3
|
+
export declare const feature: <N extends string, R, P extends AbstractFeatures>(name: N, parents: P, compute: (parents: InferParentComputeType<P>, fnode: Fnode) => R, opts?: {
|
|
4
|
+
private?: true;
|
|
5
|
+
}) => Feature<N, R, P>;
|
|
4
6
|
export declare const featureCount: <N extends string, R extends unknown[], P extends AbstractFeatures>(feat: Feature<N, R, P>) => Feature<`${N}Count`, number, {
|
|
5
7
|
[x: string]: Feature<N, R, P>;
|
|
6
8
|
}>;
|
|
@@ -12,7 +14,16 @@ type FlatFeatures<F extends AbstractFeatures> = {
|
|
|
12
14
|
[K in keyof F]: FlatFeature<F[K]>;
|
|
13
15
|
};
|
|
14
16
|
export declare const flattenFeatures: <F extends AbstractFeatures>(features: F) => FlatFeatures<F>;
|
|
15
|
-
export declare const getComputerForFeatures: (sorted
|
|
17
|
+
export declare const getComputerForFeatures: <F extends AbstractFeatures>({ sorted, features, private: privateFeatures }: {
|
|
18
|
+
sorted: readonly AbstractFeature[];
|
|
19
|
+
features: readonly string[];
|
|
20
|
+
private: readonly string[];
|
|
21
|
+
}) => FeatureComputer<FlatFeatures<F>>;
|
|
22
|
+
export declare const validateComputerInputs: ({ sorted, features, private: privateFeatures }: {
|
|
23
|
+
sorted: readonly AbstractFeature[];
|
|
24
|
+
features: readonly string[];
|
|
25
|
+
private: readonly string[];
|
|
26
|
+
}) => void;
|
|
16
27
|
export declare const topologicalSort: (features: AbstractFeatures) => AbstractFeature<unknown>[];
|
|
17
28
|
export declare const computeFeatures: <F extends AbstractFeatures>(featureComputer: FeatureComputer<F>, fnode: Fnode) => import("@protontech/autofill/types").ComputedFeatures<F>;
|
|
18
29
|
export {};
|
package/features/feature.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { utils } from "@protontech/fathom";
|
|
2
2
|
const { linearScale } = utils;
|
|
3
|
-
export const feature = (name, parents, compute) => ({
|
|
3
|
+
export const feature = (name, parents, compute, opts) => ({
|
|
4
4
|
name,
|
|
5
5
|
parents,
|
|
6
6
|
compute,
|
|
7
|
+
...(opts !== null && opts !== void 0 ? opts : {}),
|
|
7
8
|
});
|
|
8
9
|
export const featureCount = (feat) => feature(`${feat.name}Count`, { [feat.name]: feat }, (parents) => parents[feat.name].length);
|
|
9
10
|
export const featureScaled = (feat, min, max) => feature(`${feat.name}Scaled`, { [feat.name]: feat }, (parents) => linearScale(parents[feat.name], min, max));
|
|
@@ -16,17 +17,35 @@ export const featuresProduct = (feat1, feat2) => feature(`${feat1.name},${feat2.
|
|
|
16
17
|
[feat2.name]: feat2,
|
|
17
18
|
}, (parents) => Number(parents[feat1.name]) * Number(parents[feat2.name]));
|
|
18
19
|
export const flattenFeatures = (features) => features;
|
|
19
|
-
export const getComputerForFeatures = (sorted,
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
20
|
+
export const getComputerForFeatures = ({ sorted, features, private: privateFeatures }) => ({
|
|
21
|
+
compute: (fnode) => {
|
|
22
|
+
const parents = {};
|
|
23
|
+
const result = {};
|
|
24
|
+
for (const feature of sorted)
|
|
25
|
+
parents[feature.name] = feature.compute(parents, fnode);
|
|
26
|
+
for (const name of features)
|
|
27
|
+
result[name] = parents[name];
|
|
28
|
+
for (const name of privateFeatures)
|
|
29
|
+
result[name] = parents[name];
|
|
30
|
+
return result;
|
|
31
|
+
},
|
|
32
|
+
features,
|
|
33
|
+
});
|
|
34
|
+
export const validateComputerInputs = ({ sorted, features, private: privateFeatures }) => {
|
|
35
|
+
const computed = new Set();
|
|
36
|
+
for (const f of sorted) {
|
|
37
|
+
if (computed.has(f.name))
|
|
38
|
+
throw new Error(`duplicate feature '${f.name}' in sorted DAG`);
|
|
39
|
+
for (const p of Object.values(f.parents)) {
|
|
40
|
+
if (!computed.has(p.name))
|
|
41
|
+
throw new Error(`feature '${f.name}' precedes its parent '${p.name}' in sorted DAG`);
|
|
42
|
+
}
|
|
43
|
+
computed.add(f.name);
|
|
44
|
+
}
|
|
45
|
+
for (const name of [...features, ...privateFeatures]) {
|
|
46
|
+
if (!computed.has(name))
|
|
47
|
+
throw new Error(`output '${name}' missing from sorted DAG`);
|
|
48
|
+
}
|
|
30
49
|
};
|
|
31
50
|
export const topologicalSort = (features) => {
|
|
32
51
|
var _a;
|
|
@@ -43,8 +62,10 @@ export const topologicalSort = (features) => {
|
|
|
43
62
|
else {
|
|
44
63
|
Object.values(node.parents).forEach((parent) => {
|
|
45
64
|
var _a, _b, _c, _d;
|
|
46
|
-
if (!seen.has(parent))
|
|
65
|
+
if (!seen.has(parent)) {
|
|
47
66
|
to_visit.push(parent);
|
|
67
|
+
seen.add(parent);
|
|
68
|
+
}
|
|
48
69
|
(_b = (_a = childToParents.get(node)) === null || _a === void 0 ? void 0 : _a.add(parent)) !== null && _b !== void 0 ? _b : childToParents.set(node, new Set([parent]));
|
|
49
70
|
(_d = (_c = parentToChildren.get(parent)) === null || _c === void 0 ? void 0 : _c.add(node)) !== null && _d !== void 0 ? _d : parentToChildren.set(parent, new Set([node]));
|
|
50
71
|
});
|
|
@@ -65,10 +86,11 @@ export const topologicalSort = (features) => {
|
|
|
65
86
|
sorted.push(node);
|
|
66
87
|
}
|
|
67
88
|
if (childToParents.size > 0) {
|
|
68
|
-
throw new Error(`loop detected with elements:\n ${Array.from(childToParents.entries()
|
|
89
|
+
throw new Error(`loop detected with elements:\n ${Array.from(childToParents.entries())
|
|
90
|
+
.map(([child, parents]) => `${child.name} -> {${Array.from(parents)
|
|
69
91
|
.map((parent) => parent.name)
|
|
70
92
|
.sort()
|
|
71
|
-
.join(", ")}}`)
|
|
93
|
+
.join(", ")}}`)
|
|
72
94
|
.sort()
|
|
73
95
|
.join("\n ")}`);
|
|
74
96
|
}
|
package/features/feature.spec.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { feature, featuresProduct, getComputerForFeatures, topologicalSort } from "./feature";
|
|
1
|
+
import { feature, featuresProduct, getComputerForFeatures, topologicalSort, validateComputerInputs } from "./feature";
|
|
2
2
|
function doNothing() {
|
|
3
3
|
}
|
|
4
4
|
describe("topological sort", () => {
|
|
@@ -19,6 +19,16 @@ describe("topological sort", () => {
|
|
|
19
19
|
expect(nodeNames[5]).toEqual("E");
|
|
20
20
|
expect(nodeNames.slice(6, 8).sort()).toEqual(["D", "G"]);
|
|
21
21
|
});
|
|
22
|
+
test("a node reachable from multiple paths appears only once", () => {
|
|
23
|
+
const X = feature("X", {}, doNothing);
|
|
24
|
+
const Y = feature("Y", { X }, doNothing);
|
|
25
|
+
const A = feature("A", { X, Y }, doNothing);
|
|
26
|
+
const sorted = topologicalSort({ A });
|
|
27
|
+
expect(sorted.length).toBe(new Set(sorted).size);
|
|
28
|
+
const pos = new Map(sorted.map((f, i) => [f, i]));
|
|
29
|
+
expect(pos.get(X)).toBeLessThan(pos.get(Y));
|
|
30
|
+
expect(pos.get(Y)).toBeLessThan(pos.get(A));
|
|
31
|
+
});
|
|
22
32
|
test("it fails if there is a loop", () => {
|
|
23
33
|
const A = feature("A", {}, doNothing);
|
|
24
34
|
const B = feature("B", { A }, doNothing);
|
|
@@ -39,21 +49,60 @@ describe("topological sort", () => {
|
|
|
39
49
|
G -> {E}`);
|
|
40
50
|
});
|
|
41
51
|
});
|
|
52
|
+
const buildComputer = (outputs) => getComputerForFeatures({
|
|
53
|
+
sorted: topologicalSort(outputs),
|
|
54
|
+
features: Object.values(outputs)
|
|
55
|
+
.filter((f) => !f.private)
|
|
56
|
+
.map((f) => f.name),
|
|
57
|
+
private: Object.values(outputs)
|
|
58
|
+
.filter((f) => f.private)
|
|
59
|
+
.map((f) => f.name),
|
|
60
|
+
});
|
|
42
61
|
describe("Feature engineering", () => {
|
|
43
62
|
test("featureProduct", () => {
|
|
44
63
|
const A = feature("A", {}, () => 2);
|
|
45
64
|
const B = feature("B", { A }, () => 4);
|
|
46
65
|
const C = featuresProduct(A, B);
|
|
47
|
-
|
|
48
|
-
const outputNames = Object.values(features).map((f) => f.name);
|
|
49
|
-
expect(getComputerForFeatures(topologicalSort(features), outputNames).compute({})).toStrictEqual({ A: 2, B: 4, "A,B": 8 });
|
|
66
|
+
expect(buildComputer({ A, B, C }).compute({})).toMatchObject({ A: 2, B: 4, "A,B": 8 });
|
|
50
67
|
});
|
|
51
68
|
test("feature name re-mapping", () => {
|
|
52
69
|
const A = feature("A", {}, () => 2);
|
|
53
70
|
const B = feature("_B", { A }, () => 4);
|
|
54
71
|
const C = feature("C", { A, B }, (p) => p.A + p._B);
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
72
|
+
expect(buildComputer({ A, B, C }).compute({})).toMatchObject({ A: 2, _B: 4, C: 6 });
|
|
73
|
+
});
|
|
74
|
+
describe("validateComputerInputs (gen-time guards)", () => {
|
|
75
|
+
const A = feature("A", {}, () => 1);
|
|
76
|
+
const B = feature("B", { A }, () => 2);
|
|
77
|
+
test("throws on a duplicate feature name in sorted", () => {
|
|
78
|
+
const Adup = feature("A", {}, () => 9);
|
|
79
|
+
expect(() => validateComputerInputs({ sorted: [A, Adup], features: ["A"], private: [] })).toThrow("duplicate feature 'A' in sorted DAG");
|
|
80
|
+
});
|
|
81
|
+
test("throws when a parent is ordered after its child", () => {
|
|
82
|
+
expect(() => validateComputerInputs({ sorted: [B, A], features: ["A", "B"], private: [] })).toThrow("feature 'B' precedes its parent 'A' in sorted DAG");
|
|
83
|
+
});
|
|
84
|
+
test("throws when an output name is absent from the DAG", () => {
|
|
85
|
+
expect(() => validateComputerInputs({ sorted: [A], features: ["A", "ghost"], private: [] })).toThrow("output 'ghost' missing from sorted DAG");
|
|
86
|
+
expect(() => validateComputerInputs({ sorted: [A], features: ["A"], private: ["secret"] })).toThrow("output 'secret' missing from sorted DAG");
|
|
87
|
+
});
|
|
88
|
+
test("accepts a well-formed, topologically sorted DAG", () => {
|
|
89
|
+
expect(() => validateComputerInputs({ sorted: [A, B], features: ["A", "B"], private: [] })).not.toThrow();
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
describe("private outputs", () => {
|
|
93
|
+
const Trans = feature("Trans", {}, () => 10);
|
|
94
|
+
const Priv = feature("Priv", { Trans }, (p) => p.Trans + 1, { private: true });
|
|
95
|
+
const Pub = feature("Pub", { Priv }, (p) => p.Priv + 1);
|
|
96
|
+
const computer = buildComputer({ Pub, Priv });
|
|
97
|
+
const result = computer.compute({});
|
|
98
|
+
test(".features lists only non-private outputs", () => {
|
|
99
|
+
expect(computer.features).toEqual(["Pub"]);
|
|
100
|
+
});
|
|
101
|
+
test("compute() result includes private outputs ", () => {
|
|
102
|
+
expect(result).toEqual({ Pub: 12, Priv: 11 });
|
|
103
|
+
});
|
|
104
|
+
test("compute() result excludes transients ", () => {
|
|
105
|
+
expect(result).not.toHaveProperty("Trans");
|
|
106
|
+
});
|
|
58
107
|
});
|
|
59
108
|
});
|