chore: update SQL script, dev script, and add zxcvbnjs
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
File diff suppressed because one or more lines are too long
@@ -0,0 +1,17 @@
|
||||
const { zxcvbn, zxcvbnOptions } = require('@zxcvbn-ts/core');
|
||||
const { dictionary: commonDictionary, adjacencyGraphs } = require('@zxcvbn-ts/language-common');
|
||||
const { dictionary: enDictionary, translations } = require('@zxcvbn-ts/language-en');
|
||||
|
||||
zxcvbnOptions.setOptions({
|
||||
dictionary: {
|
||||
...commonDictionary,
|
||||
...enDictionary,
|
||||
},
|
||||
graphs: adjacencyGraphs,
|
||||
translations,
|
||||
});
|
||||
|
||||
// Called from Go via goja
|
||||
globalThis.zxcvbnScore = function (password) {
|
||||
return zxcvbn(password).score;
|
||||
};
|
||||
@@ -0,0 +1,68 @@
|
||||
// Package zxcvbnjs wraps @zxcvbn-ts/core via goja (ExecJS-style) for exact
|
||||
// parity between frontend and backend password strength scoring.
|
||||
//
|
||||
// The bundled JS contains the same @zxcvbn-ts/core library used in the
|
||||
// SvelteKit frontend, so scores are identical for any given password.
|
||||
package zxcvbnjs
|
||||
|
||||
import (
|
||||
"embed"
|
||||
"fmt"
|
||||
"log"
|
||||
"sync"
|
||||
|
||||
"github.com/dop251/goja"
|
||||
)
|
||||
|
||||
//go:embed bundle.js
|
||||
var bundleFS embed.FS
|
||||
|
||||
var (
|
||||
vm *goja.Runtime
|
||||
vmOnce sync.Once
|
||||
vmErr error
|
||||
)
|
||||
|
||||
func getVM() (*goja.Runtime, error) {
|
||||
vmOnce.Do(func() {
|
||||
vm = goja.New()
|
||||
bundleData, err := bundleFS.ReadFile("bundle.js")
|
||||
if err != nil {
|
||||
vmErr = fmt.Errorf("failed to read zxcvbn bundle: %w", err)
|
||||
return
|
||||
}
|
||||
_, err = vm.RunScript("zxcvbn-bundle.js", string(bundleData))
|
||||
if err != nil {
|
||||
vmErr = fmt.Errorf("failed to evaluate zxcvbn bundle: %w", err)
|
||||
return
|
||||
}
|
||||
})
|
||||
return vm, vmErr
|
||||
}
|
||||
|
||||
// Score returns the zxcvbn-ts score (0-4) for the given password.
|
||||
// It uses the exact same @zxcvbn-ts/core library as the frontend,
|
||||
// guaranteeing identical results for the same input.
|
||||
func Score(password string) (int, error) {
|
||||
runtime, err := getVM()
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("zxcvbnjs init error: %w", err)
|
||||
}
|
||||
|
||||
zxcvbnScore, ok := goja.AssertFunction(runtime.Get("zxcvbnScore"))
|
||||
if !ok {
|
||||
return 0, fmt.Errorf("zxcvbnScore not found in JS runtime")
|
||||
}
|
||||
|
||||
result, err := zxcvbnScore(goja.Undefined(), runtime.ToValue(password))
|
||||
if err != nil {
|
||||
log.Printf("zxcvbnjs: eval error: %v", err)
|
||||
return 0, fmt.Errorf("zxcvbnScore eval failed: %w", err)
|
||||
}
|
||||
|
||||
score := int(result.ToInteger())
|
||||
if score < 0 || score > 4 {
|
||||
return 0, fmt.Errorf("unexpected zxcvbn score: %d", score)
|
||||
}
|
||||
return score, nil
|
||||
}
|
||||
Reference in New Issue
Block a user