feat: Passwordless cross-device authentication

- Arkitektur: docs/auth/passwordless-architecture.md
- Backend: iom/quixzoom-auth-service/ (FastAPI + Redis)
- Webb: quixzoom-market-pages/se/login/ (QR-kod + polling)
- App: iom/quixzoom-app/src/features/auth/ (push + deep links)

Flöde: QR-kod → app-godkännande → webb-inloggad
This commit is contained in:
Bernt
2026-07-07 07:11:50 +00:00
parent 4aa984ad74
commit 6989a98d75
61843 changed files with 5491611 additions and 872231 deletions
+3
View File
@@ -0,0 +1,3 @@
import * as core from './lib/index.js'
export = core
+6
View File
@@ -0,0 +1,6 @@
/// <reference types="node" preserve="true" />
export interface CjsRequire extends NodeJS.Require {
<T>(id: string): T;
}
export declare const cjsRequire: CjsRequire;
export declare const EXTENSIONS: string[];
+9
View File
@@ -0,0 +1,9 @@
import { createRequire } from 'node:module';
export const cjsRequire = typeof require === 'function' ? require : createRequire(import.meta.url);
const DEFAULT_EXTENSIONS = cjsRequire.extensions
?
Object.keys(cjsRequire.extensions)
:
['.js', '.json', '.node'];
export const EXTENSIONS = ['.ts', '.tsx', ...DEFAULT_EXTENSIONS];
//# sourceMappingURL=constants.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"constants.js","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAM3C,MAAM,CAAC,MAAM,UAAU,GACrB,OAAO,OAAO,KAAK,UAAU,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;AAG1E,MAAM,kBAAkB,GAAG,UAAU,CAAC,UAAU;IAC9C,CAAC;QACC,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC;IACpC,CAAC;QACC,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,CAAC,CAAA;AAE7B,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,kBAAkB,CAAC,CAAA"}
+19
View File
@@ -0,0 +1,19 @@
import { type Stats } from 'node:fs';
export declare const tryPkg: (pkg: string) => string | undefined;
export declare const isPkgAvailable: (pkg: string) => boolean;
export type FileTypeBase = 'blockDevice' | 'characterDevice' | 'directory' | 'FIFO' | 'file' | 'socket' | 'symbolicLink';
export type FileType = Capitalize<FileTypeBase> | FileTypeBase;
export type FileTypes = FileType | FileType[] | boolean | 'any';
export declare const tryFileStats: (filename?: string[] | string, type?: FileTypes, base?: string) => {
filepath: string;
stats: Stats;
} | undefined;
export declare const tryFile: (filename?: string[] | string, type?: FileTypes, base?: string) => string;
export declare const tryExtensions: (filepath: string, extensions?: string[]) => string;
export interface FindUpOptions {
entry?: string;
search?: string[] | string;
type?: FileTypes;
stop?: string;
}
export declare const findUp: (entryOrOptions?: FindUpOptions | string, options?: FindUpOptions) => string;
+67
View File
@@ -0,0 +1,67 @@
import fs, {} from 'node:fs';
import path from 'node:path';
import { EXTENSIONS, cjsRequire } from './constants.js';
export const tryPkg = (pkg) => {
try {
return cjsRequire.resolve(pkg);
}
catch { }
};
export const isPkgAvailable = (pkg) => Boolean(tryPkg(pkg));
const ANY_FILE_TYPES = new Set(['any', true]);
const isAnyFileType = (type) => ANY_FILE_TYPES.has(type);
export const tryFileStats = (filename, type = 'file', base = process.cwd()) => {
if (!type) {
type = 'file';
}
if (typeof filename === 'string') {
const filepath = path.resolve(base, filename);
let stats;
try {
stats = fs.statSync(filepath, { throwIfNoEntry: false });
}
catch { }
return stats &&
(isAnyFileType(type) ||
(Array.isArray(type) ? type : [type]).some(type => stats[`is${type[0].toUpperCase()}${type.slice(1)}`]()))
? { filepath, stats }
: undefined;
}
for (const file of filename ?? []) {
const result = tryFileStats(file, type, base);
if (result) {
return result;
}
}
};
export const tryFile = (filename, type = 'file', base) => tryFileStats(filename, type, base)?.filepath ?? '';
export const tryExtensions = (filepath, extensions = EXTENSIONS) => {
const ext = [...extensions, ''].find(ext => tryFile(filepath + ext));
return ext == null ? '' : filepath + ext;
};
export const findUp = (entryOrOptions, options) => {
if (typeof entryOrOptions === 'string') {
options = {
entry: entryOrOptions,
...options,
};
}
else if (entryOrOptions) {
options = options ? { ...entryOrOptions, ...options } : entryOrOptions;
}
let { entry = process.cwd(), search = 'package.json', type, stop, } = options ?? {};
search = Array.isArray(search) ? search : [search];
do {
const searched = tryFile(search, type, entry);
if (searched) {
return searched;
}
const lastEntry = entry;
entry = path.dirname(entry);
if (entry === lastEntry) {
break;
}
} while (!stop || entry !== stop);
return '';
};
//# sourceMappingURL=helpers.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"helpers.js","sourceRoot":"","sources":["../src/helpers.ts"],"names":[],"mappings":"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"}
+91
View File
@@ -0,0 +1,91 @@
'use strict';
var node_module = require('node:module');
var fs = require('node:fs');
var path = require('node:path');
var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
const cjsRequire = typeof require === "function" ? require : node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)));
const DEFAULT_EXTENSIONS = cjsRequire.extensions ? (
// eslint-disable-next-line sonarjs/deprecation
Object.keys(cjsRequire.extensions)
) : (
// `require.extensions` could be `undefined` - #430
[".js", ".json", ".node"]
);
const EXTENSIONS = [".ts", ".tsx", ...DEFAULT_EXTENSIONS];
const tryPkg = (pkg) => {
try {
return cjsRequire.resolve(pkg);
} catch {
}
};
const isPkgAvailable = (pkg) => Boolean(tryPkg(pkg));
const ANY_FILE_TYPES = /* @__PURE__ */ new Set(["any", true]);
const isAnyFileType = (type) => ANY_FILE_TYPES.has(type);
const tryFileStats = (filename, type = "file", base = process.cwd()) => {
if (!type) {
type = "file";
}
if (typeof filename === "string") {
const filepath = path.resolve(base, filename);
let stats;
try {
stats = fs.statSync(filepath, { throwIfNoEntry: false });
} catch {
}
return stats && (isAnyFileType(type) || (Array.isArray(type) ? type : [type]).some(
(type2) => stats[`is${type2[0].toUpperCase()}${type2.slice(1)}`]()
)) ? { filepath, stats } : void 0;
}
for (const file of filename ?? []) {
const result = tryFileStats(file, type, base);
if (result) {
return result;
}
}
};
const tryFile = (filename, type = "file", base) => tryFileStats(filename, type, base)?.filepath ?? "";
const tryExtensions = (filepath, extensions = EXTENSIONS) => {
const ext = [...extensions, ""].find((ext2) => tryFile(filepath + ext2));
return ext == null ? "" : filepath + ext;
};
const findUp = (entryOrOptions, options) => {
if (typeof entryOrOptions === "string") {
options = {
entry: entryOrOptions,
...options
};
} else if (entryOrOptions) {
options = options ? { ...entryOrOptions, ...options } : entryOrOptions;
}
let {
entry = process.cwd(),
search = "package.json",
type,
stop
} = options ?? {};
search = Array.isArray(search) ? search : [search];
do {
const searched = tryFile(search, type, entry);
if (searched) {
return searched;
}
const lastEntry = entry;
entry = path.dirname(entry);
if (entry === lastEntry) {
break;
}
} while (!stop || entry !== stop);
return "";
};
exports.EXTENSIONS = EXTENSIONS;
exports.cjsRequire = cjsRequire;
exports.findUp = findUp;
exports.isPkgAvailable = isPkgAvailable;
exports.tryExtensions = tryExtensions;
exports.tryFile = tryFile;
exports.tryFileStats = tryFileStats;
exports.tryPkg = tryPkg;
+2
View File
@@ -0,0 +1,2 @@
export * from './constants.js';
export * from './helpers.js';
+3
View File
@@ -0,0 +1,3 @@
export * from './constants.js';
export * from './helpers.js';
//# sourceMappingURL=index.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAA;AAC9B,cAAc,cAAc,CAAA"}
+38
View File
@@ -0,0 +1,38 @@
{
"name": "@pkgr/core",
"version": "0.3.6",
"type": "module",
"description": "Shared core module for `@pkgr` packages or any package else",
"repository": "git+https://github.com/un-ts/pkgr.git",
"homepage": "https://github.com/un-ts/pkgr/blob/master/packages/core",
"author": "JounQin <admin@1stg.me> (https://www.1stG.me)",
"funding": "https://opencollective.com/pkgr",
"license": "MIT",
"engines": {
"node": "^14.18.0 || >=16.0.0"
},
"main": "./lib/index.cjs",
"types": "./index.d.cts",
"module": "./lib/index.js",
"exports": {
".": {
"import": {
"types": "./lib/index.d.ts",
"default": "./lib/index.js"
},
"require": {
"types": "./index.d.cts",
"default": "./lib/index.cjs"
}
},
"./package.json": "./package.json"
},
"files": [
"index.d.cts",
"lib"
],
"publishConfig": {
"access": "public"
},
"sideEffects": false
}