rudderstash 0.1.2 → 0.1.4
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/dist/rudderstash.js +8 -8
- package/dist/test-loader.js +17 -0
- package/libs/@rs/encrypt/package.json +7 -0
- package/libs/@rs/encrypt/v1.js +63 -0
- package/libs/@rs/hash/package.json +7 -0
- package/libs/@rs/hash/v1.js +27 -0
- package/libs/@rs/localizeAppleDeviceModel/package.json +7 -0
- package/libs/@rs/localizeAppleDeviceModel/v1.js +3 -0
- package/libs/@rs/userAgentParser/package.json +7 -0
- package/libs/@rs/userAgentParser/v1.js +64 -0
- package/libs/test-loader.js +17 -0
- package/package.json +4 -2
package/dist/rudderstash.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
"use strict";import{writeFile as
|
|
2
|
+
"use strict";import{writeFile as u,readdir as J,readFile as b}from"fs/promises";import{fileURLToPath as T}from"url";import{dirname as k,join as U}from"path";import{register as E}from"node:module";import"colors";import R from"dotenv";import F from"yargs";import{hideBin as C}from"yargs/helpers";import{diffJson as x}from"diff";import{existsSync as g}from"fs";import{writeFile as O,readdir as _,readFile as m}from"fs/promises";import{createHash as L}from"crypto";var S="0.1.4",B="[aaa5a64@trunk; built: 2026-01-03T16:52:00Z]",N=class{headers={};endpoint;constructor(r){this.endpoint=r.RUDDERSTACK_API_ENDPOINT,this.headers={"User-Agent":`rudderstash/${S}`,"Content-Type":"application/json",Authorization:"Basic "+Buffer.from(`${r.RUDDERSTACK_API_USER}:${r.RUDDERSTACK_API_TOKEN}`).toString("base64")}}async get(r){return(await fetch(this.endpoint+r,{headers:this.headers})).json()}async post(r,a){return(await fetch(this.endpoint+r,{method:"POST",headers:this.headers,body:JSON.stringify(a)})).json()}},y=class A{id;versionId;createdAt;updatedAt;name;description;code="";path;constructor(a={}){this.id=a.id,this.versionId=a.versionId,this.createdAt=a.createdAt,this.updatedAt=new Date(a.updatedAt),this.name=a.name}static async get(a){const i=await _(".");for(const s of i.filter(o=>o.endsWith(".transformation.js"))){const o=await m(s,"utf-8");if(!g(`${s}on`))continue;const t=JSON.parse(await m(`${s}on`,"utf-8"));if(t.id===a){const e=new A(t);return e.code=o,e}}return null}hash(){return L("sha1").update(JSON.stringify({id:this.id,code:this.code})).digest("hex")}},w=class I{transformations=[];api;constructor(a){this.api=new N(a)}static async read(a){const i=new I(a),s=await _(".");for(const o of s.filter(t=>t.endsWith(".transformation.js"))){if(!g(`${o}on`)){const n=new y;n.name=o.split(".transformation.js").at(0),n.code=await m(o,"utf-8"),n.path=o,i.transformations.push(n);continue}const t=JSON.parse(await m(`${o}on`,"utf-8")),e=await y.get(t.id);e&&(e.path=o,i.transformations.push(e))}return await i.fetch(),i}async fetch(){g(".ruddercache")||await O(".ruddercache","{}");const a=JSON.parse(await m(".ruddercache","utf8"));a.transformations=(await this.api.get("/transformations")).transformations,a.transformations_fetched_at=Date.now(),await O(".ruddercache",JSON.stringify(a))}async list(){g(".ruddercache")||await O(".ruddercache","{}");let a=JSON.parse(await m(".ruddercache","utf8"));a.transformations===void 0&&(this.fetch(),a=JSON.parse(await m(".ruddercache","utf8")));const i=a.transformations,s={},o={};for(const t of i){const e=t.id;s[e]=new y(t),s[e].code=t.code}for(const t of this.transformations){const e=t.id??crypto.randomUUID();t.id=e,o[e]||(o[e]=[]),o[e].push(t)}return{transformations_fetched_at:a.transformations_fetched_at,transformations:[s,o]}}},d={};R.config({quiet:!0,processEnv:d,override:!1}),Object.entries(d).forEach(([r,a])=>{r in process.env&&(d[r]=process.env[r])});var K=await F(C(process.argv)).parse(),[W,...et]=K._;switch(W){case"version":{console.info(`rudderstash ${S} ${B}`);break}case"status":{const a=await(await w.read(d)).list(),[i,s]=a.transformations;if(console.info(`Last fetched ${new Date(a.transformations_fetched_at)}
|
|
3
3
|
`),console.info("Upstream:"),!Object.keys(i).length)console.info(` (none)
|
|
4
|
-
`);else for(const o of Object.values(i)){let t=" ";const e=[];if(s[o.id]===void 0)t="+";else{const n=(s[o.id]??[]).reduce((f,h)=>f||h.hash()!==o.hash(),!1),
|
|
4
|
+
`);else for(const o of Object.values(i)){let t=" ";const e=[];if(s[o.id]===void 0)t="+";else{const n=(s[o.id]??[]).reduce((f,h)=>f||h.hash()!==o.hash(),!1),c=(s[o.id]??[]).reduce((f,h)=>f||h.name!==o.name||h.description!==o.description,!1);n&&(t="~"),c&&(t="~",e.push("metadata changed")),!n&&!c&&e.push("no changes")}console.info(` ${t} ${o.hash().substring(0,7)} ${o.name}`+(e.length?` (${e.join(", ")})`:""))}if(console.info(`
|
|
5
5
|
Local:`),!Object.keys(s).length)console.info(` (none)
|
|
6
|
-
`);else for(const o of Object.values(s))for(const t of o){let e=" ",n=[];o.filter(f=>f.id===t.id).length>1&&(e="!",n.push("duplicate")),i[t.id]===void 0&&(e="+"),console.info(` ${e} ${t.hash().substring(0,7)} ${t.name} (${t.path})`+(n.length?` (${n.join(", ")})`:""))}break}case"pull":{const a=await(await
|
|
7
|
-
`)+". Resolve by leaving only one.".red);continue}const e=await
|
|
8
|
-
`).map(
|
|
9
|
-
`)),
|
|
10
|
-
`).map(
|
|
11
|
-
`))}
|
|
6
|
+
`);else for(const o of Object.values(s))for(const t of o){let e=" ",n=[];o.filter(f=>f.id===t.id).length>1&&(e="!",n.push("duplicate")),i[t.id]===void 0&&(e="+"),console.info(` ${e} ${t.hash().substring(0,7)} ${t.name} (${t.path})`+(n.length?` (${n.join(", ")})`:""))}break}case"pull":{const a=await(await w.read(d)).list(),[i,s]=a.transformations;if(Object.keys(i).length<1){console.info("No transformations upstream, nothing to pull.");break}for(const o of Object.values(i))if(s[o.id])for(const t of Object.values(s[o.id])){const e=t.path;console.info(`< Pulling new transformation ${o.name} to ${e}...`),await u(e,o.code),console.info(`< Saving metadata to ${e}on.`),delete o.code,await u(e+"on",JSON.stringify(o,null,2))}else{const t=o.name.split(" ").map(e=>e[0].toUpperCase()+e.substring(1)).join("")+".transformation.js";console.info(`< Pulling new transformation ${o.name} to ${t}...`),await u(t,o.code),console.info(`< Saving metadata to ${t}on.`),delete o.code,await u(t+"on",JSON.stringify(o,null,2))}break}case"push":{const a=await(await w.read(d)).list(),[i,s]=a.transformations;if(Object.values(s).length<1){console.info("No local transformations, nothing to push.");break}const o=new N(d);for(const t of Object.values(s)){if(Object.values(t).length>1){console.warn("Conflict! The following local transformations have the same ID: "+Object.values(t).map(f=>f.path).join(`
|
|
7
|
+
`)+". Resolve by leaving only one.".red);continue}const e=await b(t[0].path,"utf-8");let n={};if(t[0].versionId===void 0?(n.id="",n.name=t[0].path.split(".transformation.js").at(0),n.description=t[0].path):n=JSON.parse(await b(`${t[0].path}on`,"utf-8")),i[t[0].id]&&t[0].hash()===i[t[0].id].hash()){console.info(`> Skipping ${n.name} transformation. No changes.`);continue}console.info(`> Pushing ${e.length} bytes of code to ${n.name} transformation...`);const c=await o.post(`/transformations/${n.id}?publish=true`,{code:e,name:n.name,language:"javascript",description:n.description});if(c.error!==void 0){console.error(c.error);continue}delete c.code,await u(`${t[0].path}on`,JSON.stringify({...n,...c},null,2)),console.info(`> Published with version ${c.versionId}.`),console.info("< Metadata updated.")}break}case"diff":{const a=await(await w.read(d)).list()}case"revert":break;case"test":{const a=await(await w.read(d)).list(),[i,s]=a.transformations,o=(await J(".")).filter(n=>n.endsWith(".tests.json"));o.length<1&&(console.error("No tests to run [*.tests.json].".red),process.exit(-1));for(const n of o)Object.values(s).flat().find(f=>f.path===n.replace(".tests.json",".transformation.js"))===void 0&&(console.error(`${n} has no matching ${n.replace(".tests.json",".transformation.js")} file.`.red),process.exit(-1));const t=k(T(import.meta.url)),e=U(t,"test-loader.js");E(e,import.meta.url);for(const n of o){const c=Object.values(s).flat().find(l=>l.path===n.replace(".tests.json",".transformation.js"));console.info(`${c.name}:`);const f=JSON.parse(await b(n,"utf8")),D=await import(`file://${process.cwd()}/${c.path}`);for(const l of f){const P=D.transformEvent(l.input,()=>Object.assign({destinationId:void 0,destinationName:void 0,destinationType:void 0,sourceId:void 0,sourceName:void 0,sourceType:void 0},l.metadata??{})),v=x(l.expected??{_is_null:!0},P??{_is_null:!0});let j="FAIL";if(v.length===1&&!v[0].added&&!v[0].removed&&(j="PASS"),j==="FAIL"){console.warn(` ${j} - ${n.split(".json").at(0)}: ${l.name} `.red);for(const p of v){if(!p.added&&!p.removed){console.warn();continue}p.added&&console.warn(p.value.trim().split(`
|
|
8
|
+
`).map($=>` + ${$}`).join(`
|
|
9
|
+
`)),p.removed&&console.warn(p.value.trim().split(`
|
|
10
|
+
`).map($=>` - ${$}`).join(`
|
|
11
|
+
`))}}else console.info(` ${j} - ${n.split(".json").at(0)}: ${l.name} `.green)}}break}default:console.error("Unknown command. Did you mean any of these: status, pull, push, test?".red)}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
2
|
+
import { dirname, join } from "node:path";
|
|
3
|
+
|
|
4
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
5
|
+
|
|
6
|
+
export async function resolve(specifier, context, nextResolve) {
|
|
7
|
+
if (specifier.startsWith("@rs/")) {
|
|
8
|
+
// @rs/hash/v1 -> ../libs/@rs/hash/v1.js (relative to dist/)
|
|
9
|
+
const libPath = join(__dirname, "..", "libs", "@rs", specifier.slice(4) + ".js");
|
|
10
|
+
return {
|
|
11
|
+
shortCircuit: true,
|
|
12
|
+
url: pathToFileURL(libPath).href,
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
return nextResolve(specifier, context);
|
|
17
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
export function JSEncrypt(options) {
|
|
2
|
+
if (!(this instanceof JSEncrypt)) {
|
|
3
|
+
return new JSEncrypt(options);
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
this.key = null;
|
|
7
|
+
|
|
8
|
+
this.setKey = function(key) {
|
|
9
|
+
this.key = key;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
this.setPrivateKey = function(key) {
|
|
13
|
+
this.key = key;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
this.setPublicKey = function(key) {
|
|
17
|
+
this.key = key;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
this.encrypt = function(text) {
|
|
21
|
+
return btoa(text);
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
this.decrypt = function(text) {
|
|
25
|
+
try {
|
|
26
|
+
return atob(text);
|
|
27
|
+
} catch {
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
this.sign = function(text, digestMethod, digestName) {
|
|
33
|
+
return btoa(text);
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
this.verify = function(text, signature, digestName) {
|
|
37
|
+
return true;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
this.getKey = function() {
|
|
41
|
+
return this.key;
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
this.getPrivateKey = function() {
|
|
45
|
+
return "-----BEGIN RSA PRIVATE KEY-----\nSTUB\n-----END RSA PRIVATE KEY-----";
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
this.getPrivateKeyB64 = function() {
|
|
49
|
+
return "U1RVQg==";
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
this.getPublicKey = function() {
|
|
53
|
+
return "-----BEGIN PUBLIC KEY-----\nSTUB\n-----END PUBLIC KEY-----";
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
this.getPublicKeyB64 = function() {
|
|
57
|
+
return "U1RVQg==";
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
return this;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
JSEncrypt.version = "1.0.0-stub";
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { createHash } from "crypto";
|
|
2
|
+
|
|
3
|
+
export function sha256(data) {
|
|
4
|
+
return createHash("sha256").update(data).digest("hex");
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export function sha1(data) {
|
|
8
|
+
return createHash("sha1").update(data).digest("hex");
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function md5(data) {
|
|
12
|
+
return createHash("md5").update(data).digest("hex");
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function cyrb53(str, seed = 0) {
|
|
16
|
+
let h1 = 0xdeadbeef ^ seed, h2 = 0x41c6ce57 ^ seed;
|
|
17
|
+
for (let i = 0, ch; i < str.length; i++) {
|
|
18
|
+
ch = str.charCodeAt(i);
|
|
19
|
+
h1 = Math.imul(h1 ^ ch, 2654435761);
|
|
20
|
+
h2 = Math.imul(h2 ^ ch, 1597334677);
|
|
21
|
+
}
|
|
22
|
+
h1 = Math.imul(h1 ^ (h1 >>> 16), 2246822507);
|
|
23
|
+
h1 ^= Math.imul(h2 ^ (h2 >>> 13), 3266489909);
|
|
24
|
+
h2 = Math.imul(h2 ^ (h2 >>> 16), 2246822507);
|
|
25
|
+
h2 ^= Math.imul(h1 ^ (h1 >>> 13), 3266489909);
|
|
26
|
+
return 4294967296 * (2097151 & h2) + (h1 >>> 0);
|
|
27
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
export function UAParser(ua, extensions) {
|
|
2
|
+
if (!(this instanceof UAParser)) {
|
|
3
|
+
return new UAParser(ua, extensions).getResult();
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
const _ua = ua || "";
|
|
7
|
+
|
|
8
|
+
this.getBrowser = function() {
|
|
9
|
+
return {
|
|
10
|
+
name: "Chrome",
|
|
11
|
+
version: "120.0.0.0",
|
|
12
|
+
major: "120"
|
|
13
|
+
};
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
this.getCPU = function() {
|
|
17
|
+
return {
|
|
18
|
+
architecture: "amd64"
|
|
19
|
+
};
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
this.getDevice = function() {
|
|
23
|
+
return {
|
|
24
|
+
vendor: undefined,
|
|
25
|
+
model: undefined,
|
|
26
|
+
type: undefined
|
|
27
|
+
};
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
this.getEngine = function() {
|
|
31
|
+
return {
|
|
32
|
+
name: "Blink",
|
|
33
|
+
version: "120.0.0.0"
|
|
34
|
+
};
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
this.getOS = function() {
|
|
38
|
+
return {
|
|
39
|
+
name: "Mac OS",
|
|
40
|
+
version: "10.15.7"
|
|
41
|
+
};
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
this.getUA = function() {
|
|
45
|
+
return _ua;
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
this.setUA = function(newUa) {
|
|
49
|
+
return this;
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
this.getResult = function() {
|
|
53
|
+
return {
|
|
54
|
+
ua: this.getUA(),
|
|
55
|
+
browser: this.getBrowser(),
|
|
56
|
+
engine: this.getEngine(),
|
|
57
|
+
os: this.getOS(),
|
|
58
|
+
device: this.getDevice(),
|
|
59
|
+
cpu: this.getCPU()
|
|
60
|
+
};
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
return this;
|
|
64
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
2
|
+
import { dirname, join } from "node:path";
|
|
3
|
+
|
|
4
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
5
|
+
|
|
6
|
+
export async function resolve(specifier, context, nextResolve) {
|
|
7
|
+
if (specifier.startsWith("@rs/")) {
|
|
8
|
+
// @rs/hash/v1 -> ../libs/@rs/hash/v1.js (relative to dist/)
|
|
9
|
+
const libPath = join(__dirname, "..", "libs", "@rs", specifier.slice(4) + ".js");
|
|
10
|
+
return {
|
|
11
|
+
shortCircuit: true,
|
|
12
|
+
url: pathToFileURL(libPath).href,
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
return nextResolve(specifier, context);
|
|
17
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rudderstash",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
4
|
"description": "Rudderstack transformation version control, deployment and testing.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"rudderstack"
|
|
@@ -28,7 +28,9 @@
|
|
|
28
28
|
"test": "echo \"Error: no test specified\" && exit 1"
|
|
29
29
|
},
|
|
30
30
|
"files": [
|
|
31
|
-
"dist/rudderstash.js"
|
|
31
|
+
"dist/rudderstash.js",
|
|
32
|
+
"dist/test-loader.js",
|
|
33
|
+
"libs"
|
|
32
34
|
],
|
|
33
35
|
"devDependencies": {
|
|
34
36
|
"@types/node": "^25.0.3",
|