@qrxcode/js 0.1.0

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/README.md ADDED
@@ -0,0 +1,113 @@
1
+ # QRX flow discovery SDK
2
+
3
+ Detect RSS, Atom and JSON Feed flows from normal website and page URLs.
4
+
5
+ With QRX, applications with QR scanners can interact with QR codes
6
+ more like browsers interact with links.
7
+
8
+ QRX works with:
9
+
10
+ - QR code scanners
11
+ - pasted links
12
+ - shared URLs
13
+ - typed URLs
14
+ - browser extensions
15
+ - app share sheets
16
+ - any workflow that receives a URL
17
+
18
+ A normal QR code still contains a normal URL.
19
+
20
+ QRX-compatible applications resolve the URL,
21
+ discover machine-readable flows from the HTML `<head>`,
22
+ and let the application decide what to do next.
23
+
24
+ This works especially well with podcast websites,
25
+ because many podcast sites already expose one or multiple feeds.
26
+
27
+ With QRX, podcast subscriptions can become as simple
28
+ and natural as following someone on social media.
29
+
30
+ Examples of flows:
31
+
32
+ - RSS feeds
33
+ - Atom feeds
34
+ - JSON feeds
35
+
36
+ At the moment, "flow" is a broad term used by QRX
37
+ to describe machine-readable update streams and feeds.
38
+
39
+ QRX does not replace RSS or existing feed technologies.
40
+
41
+ QRX helps applications discover and work with them more naturally.
42
+
43
+ Learn more at https://qrx.dev
44
+
45
+ ## Install
46
+
47
+ ```bash
48
+ npm install @qrxcode/js
49
+ ```
50
+
51
+ ## Usage
52
+
53
+ ```js
54
+ import { resolveQRX } from "@qrxcode/js";
55
+
56
+ const result = await resolveQRX(
57
+ "https://podnews.net"
58
+ );
59
+
60
+ console.log(result.flows);
61
+ ```
62
+
63
+ ## Example output
64
+
65
+ ```js
66
+ [
67
+ {
68
+ type: "rss",
69
+ url: "https://podnews.net/rss"
70
+ },
71
+ {
72
+ type: "jsonfeed",
73
+ url: "https://podnews.net/feed.json"
74
+ }
75
+ ]
76
+ ```
77
+
78
+ ## Supported flow types
79
+
80
+ * RSS
81
+ * Atom
82
+ * JSON Feed
83
+
84
+ ## Supported discovery methods
85
+
86
+ ```html
87
+ <link
88
+ rel="alternate"
89
+ type="application/rss+xml"
90
+ href="/feed.xml">
91
+ ```
92
+
93
+ ```html
94
+ <link
95
+ rel="alternate"
96
+ type="application/atom+xml"
97
+ href="/atom.xml">
98
+ ```
99
+
100
+ ```html
101
+ <link
102
+ rel="alternate"
103
+ type="application/feed+json"
104
+ href="/feed.json">
105
+ ```
106
+
107
+ ## Philosophy
108
+
109
+ QRX does not change QR codes.
110
+
111
+ With QRX, sources can expose machine-readable flows,
112
+ and applications can understand and interact with them naturally.
113
+
package/dist/index.cjs ADDED
@@ -0,0 +1,93 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ detectFlows: () => detectFlows,
24
+ resolveQRX: () => resolveQRX,
25
+ selectFlows: () => selectFlows
26
+ });
27
+ module.exports = __toCommonJS(index_exports);
28
+
29
+ // src/detect.ts
30
+ var FLOW_TYPES = {
31
+ "application/rss+xml": "rss",
32
+ "application/atom+xml": "atom",
33
+ "application/feed+json": "jsonfeed"
34
+ };
35
+ function detectFlows(html, sourceUrl) {
36
+ const flows = [];
37
+ const linkTagRegex = /<link\s+[^>]*>/gi;
38
+ const tags = html.match(linkTagRegex) || [];
39
+ for (const tag of tags) {
40
+ const rel = getAttribute(tag, "rel");
41
+ const type = getAttribute(tag, "type");
42
+ const href = getAttribute(tag, "href");
43
+ if (!rel || !type || !href) continue;
44
+ if (!rel.includes("alternate")) continue;
45
+ const flowType = FLOW_TYPES[type];
46
+ if (!flowType) continue;
47
+ flows.push({
48
+ type: flowType,
49
+ url: new URL(href, sourceUrl).toString()
50
+ });
51
+ }
52
+ return dedupeFlows(flows);
53
+ }
54
+ function getAttribute(tag, attribute) {
55
+ const regex = new RegExp(
56
+ `${attribute}=["']([^"']+)["']`,
57
+ "i"
58
+ );
59
+ const match = tag.match(regex);
60
+ return match ? match[1] : null;
61
+ }
62
+ function dedupeFlows(flows) {
63
+ const seen = /* @__PURE__ */ new Set();
64
+ return flows.filter((flow) => {
65
+ const key = `${flow.type}:${flow.url}`;
66
+ if (seen.has(key)) return false;
67
+ seen.add(key);
68
+ return true;
69
+ });
70
+ }
71
+
72
+ // src/resolve.ts
73
+ async function resolveQRX(url) {
74
+ const response = await fetch(url);
75
+ const html = await response.text();
76
+ const flows = detectFlows(html, response.url);
77
+ return {
78
+ flows
79
+ };
80
+ }
81
+
82
+ // src/select.ts
83
+ function selectFlows(flows, preferredTypes) {
84
+ return flows.filter(
85
+ (flow) => preferredTypes.includes(flow.type)
86
+ );
87
+ }
88
+ // Annotate the CommonJS export names for ESM import in node:
89
+ 0 && (module.exports = {
90
+ detectFlows,
91
+ resolveQRX,
92
+ selectFlows
93
+ });
@@ -0,0 +1,16 @@
1
+ type FlowType = "rss" | "atom" | "jsonfeed";
2
+ interface Flow {
3
+ type: FlowType;
4
+ url: string;
5
+ }
6
+ interface QRXResult {
7
+ flows: Flow[];
8
+ }
9
+
10
+ declare function detectFlows(html: string, sourceUrl: string): Flow[];
11
+
12
+ declare function resolveQRX(url: string): Promise<QRXResult>;
13
+
14
+ declare function selectFlows(flows: Flow[], preferredTypes: FlowType[]): Flow[];
15
+
16
+ export { type Flow, type FlowType, type QRXResult, detectFlows, resolveQRX, selectFlows };
@@ -0,0 +1,16 @@
1
+ type FlowType = "rss" | "atom" | "jsonfeed";
2
+ interface Flow {
3
+ type: FlowType;
4
+ url: string;
5
+ }
6
+ interface QRXResult {
7
+ flows: Flow[];
8
+ }
9
+
10
+ declare function detectFlows(html: string, sourceUrl: string): Flow[];
11
+
12
+ declare function resolveQRX(url: string): Promise<QRXResult>;
13
+
14
+ declare function selectFlows(flows: Flow[], preferredTypes: FlowType[]): Flow[];
15
+
16
+ export { type Flow, type FlowType, type QRXResult, detectFlows, resolveQRX, selectFlows };
package/dist/index.js ADDED
@@ -0,0 +1,64 @@
1
+ // src/detect.ts
2
+ var FLOW_TYPES = {
3
+ "application/rss+xml": "rss",
4
+ "application/atom+xml": "atom",
5
+ "application/feed+json": "jsonfeed"
6
+ };
7
+ function detectFlows(html, sourceUrl) {
8
+ const flows = [];
9
+ const linkTagRegex = /<link\s+[^>]*>/gi;
10
+ const tags = html.match(linkTagRegex) || [];
11
+ for (const tag of tags) {
12
+ const rel = getAttribute(tag, "rel");
13
+ const type = getAttribute(tag, "type");
14
+ const href = getAttribute(tag, "href");
15
+ if (!rel || !type || !href) continue;
16
+ if (!rel.includes("alternate")) continue;
17
+ const flowType = FLOW_TYPES[type];
18
+ if (!flowType) continue;
19
+ flows.push({
20
+ type: flowType,
21
+ url: new URL(href, sourceUrl).toString()
22
+ });
23
+ }
24
+ return dedupeFlows(flows);
25
+ }
26
+ function getAttribute(tag, attribute) {
27
+ const regex = new RegExp(
28
+ `${attribute}=["']([^"']+)["']`,
29
+ "i"
30
+ );
31
+ const match = tag.match(regex);
32
+ return match ? match[1] : null;
33
+ }
34
+ function dedupeFlows(flows) {
35
+ const seen = /* @__PURE__ */ new Set();
36
+ return flows.filter((flow) => {
37
+ const key = `${flow.type}:${flow.url}`;
38
+ if (seen.has(key)) return false;
39
+ seen.add(key);
40
+ return true;
41
+ });
42
+ }
43
+
44
+ // src/resolve.ts
45
+ async function resolveQRX(url) {
46
+ const response = await fetch(url);
47
+ const html = await response.text();
48
+ const flows = detectFlows(html, response.url);
49
+ return {
50
+ flows
51
+ };
52
+ }
53
+
54
+ // src/select.ts
55
+ function selectFlows(flows, preferredTypes) {
56
+ return flows.filter(
57
+ (flow) => preferredTypes.includes(flow.type)
58
+ );
59
+ }
60
+ export {
61
+ detectFlows,
62
+ resolveQRX,
63
+ selectFlows
64
+ };
package/package.json ADDED
@@ -0,0 +1,51 @@
1
+ {
2
+ "name": "@qrxcode/js",
3
+ "version": "0.1.0",
4
+ "description": "QRX flow discovery SDK: scan once, app understands.",
5
+ "homepage": "https://qrx.dev",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/qrxcode/qrx-js.git"
9
+ },
10
+ "bugs": {
11
+ "url": "https://github.com/qrxcode/qrx-js/issues"
12
+ },
13
+ "type": "module",
14
+ "main": "./dist/index.cjs",
15
+ "module": "./dist/index.js",
16
+ "types": "./dist/index.d.ts",
17
+ "exports": {
18
+ ".": {
19
+ "types": "./dist/index.d.ts",
20
+ "import": "./dist/index.js",
21
+ "require": "./dist/index.cjs"
22
+ }
23
+ },
24
+ "files": ["dist", "README.md"],
25
+ "scripts": {
26
+ "build": "tsup src/index.ts --format esm,cjs --dts",
27
+ "test": "vitest run",
28
+ "dev": "vitest",
29
+ "prepublishOnly": "npm run test && npm run build"
30
+ },
31
+ "keywords": [
32
+ "qrx",
33
+ "qr",
34
+ "rss",
35
+ "atom",
36
+ "jsonfeed",
37
+ "feed",
38
+ "podcast",
39
+ "scanner",
40
+ "discovery",
41
+ "directflow"
42
+ ],
43
+ "author": "Yeldar Kudaibergen",
44
+ "license": "MIT",
45
+ "devDependencies": {
46
+ "@types/node": "latest",
47
+ "tsup": "latest",
48
+ "typescript": "latest",
49
+ "vitest": "latest"
50
+ }
51
+ }