@flamingo-stack/openframe-frontend-core 0.0.436 → 0.0.437
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/{chunk-32V2VHPE.cjs → chunk-33D5O7AD.cjs} +26 -26
- package/dist/{chunk-32V2VHPE.cjs.map → chunk-33D5O7AD.cjs.map} +1 -1
- package/dist/{chunk-FSOKLOXF.js → chunk-6HROGXG6.js} +2 -2
- package/dist/{chunk-E6NRV33G.cjs → chunk-7EO6RAHH.cjs} +11 -11
- package/dist/{chunk-E6NRV33G.cjs.map → chunk-7EO6RAHH.cjs.map} +1 -1
- package/dist/{chunk-2WXQFYNC.js → chunk-7NULUAOH.js} +2 -2
- package/dist/{chunk-NRIJNBRG.cjs → chunk-7Z26YAR3.cjs} +26 -26
- package/dist/{chunk-NRIJNBRG.cjs.map → chunk-7Z26YAR3.cjs.map} +1 -1
- package/dist/{chunk-2QJNX32P.cjs → chunk-CU5PG6Z2.cjs} +5 -5
- package/dist/{chunk-2QJNX32P.cjs.map → chunk-CU5PG6Z2.cjs.map} +1 -1
- package/dist/{chunk-LYK6D7GU.js → chunk-CWQQV4YF.js} +2 -2
- package/dist/{chunk-WXNE2BF4.cjs → chunk-EKPUC5NC.cjs} +37 -37
- package/dist/{chunk-WXNE2BF4.cjs.map → chunk-EKPUC5NC.cjs.map} +1 -1
- package/dist/{chunk-3OPSNUPJ.js → chunk-F24JBANX.js} +2 -2
- package/dist/{chunk-PXTDEKAY.cjs → chunk-FF4SI6PW.cjs} +7 -7
- package/dist/{chunk-PXTDEKAY.cjs.map → chunk-FF4SI6PW.cjs.map} +1 -1
- package/dist/{chunk-MWIYAVFT.js → chunk-GN54QCPI.js} +4 -4
- package/dist/{chunk-ZH2OEMQJ.js → chunk-HL62CQJC.js} +34 -1
- package/dist/chunk-HL62CQJC.js.map +1 -0
- package/dist/{chunk-KVXB3FOB.cjs → chunk-IVIRESMD.cjs} +62 -62
- package/dist/{chunk-KVXB3FOB.cjs.map → chunk-IVIRESMD.cjs.map} +1 -1
- package/dist/{chunk-AUSUEKXY.cjs → chunk-JEPZKBV5.cjs} +31 -31
- package/dist/{chunk-AUSUEKXY.cjs.map → chunk-JEPZKBV5.cjs.map} +1 -1
- package/dist/{chunk-P5EN67AU.cjs → chunk-LAZYT543.cjs} +59 -59
- package/dist/{chunk-P5EN67AU.cjs.map → chunk-LAZYT543.cjs.map} +1 -1
- package/dist/{chunk-32BSKGEI.js → chunk-M6RZRURG.js} +2 -2
- package/dist/{chunk-DVYA3BDU.js → chunk-O5TONTLW.js} +4 -4
- package/dist/{chunk-E77Z2P3K.js → chunk-OY2LB5VP.js} +4 -4
- package/dist/{chunk-GQHXJ37C.js → chunk-PMANI6KY.js} +2 -2
- package/dist/{chunk-NGBO5Z5O.cjs → chunk-PWQNLW4I.cjs} +15 -15
- package/dist/{chunk-NGBO5Z5O.cjs.map → chunk-PWQNLW4I.cjs.map} +1 -1
- package/dist/{chunk-BMEA5NPX.js → chunk-QAIGNMPP.js} +4 -4
- package/dist/{chunk-NFU37EP6.js → chunk-U5OADJNL.js} +2 -2
- package/dist/{chunk-IWYRSGJU.cjs → chunk-UXYKFAZS.cjs} +9 -9
- package/dist/{chunk-IWYRSGJU.cjs.map → chunk-UXYKFAZS.cjs.map} +1 -1
- package/dist/{chunk-3V5UQ5TU.cjs → chunk-YQOIYNBJ.cjs} +35 -2
- package/dist/chunk-YQOIYNBJ.cjs.map +1 -0
- package/dist/components/case-studies/index.cjs +8 -8
- package/dist/components/case-studies/index.js +2 -2
- package/dist/components/chat/index.cjs +2 -2
- package/dist/components/chat/index.js +1 -1
- package/dist/components/contact/index.cjs +3 -3
- package/dist/components/contact/index.js +2 -2
- package/dist/components/docs/index.cjs +5 -5
- package/dist/components/docs/index.js +4 -4
- package/dist/components/embeds/index.cjs +3 -3
- package/dist/components/embeds/index.js +2 -2
- package/dist/components/faq/index.cjs +3 -3
- package/dist/components/faq/index.js +2 -2
- package/dist/components/features/index.cjs +2 -2
- package/dist/components/features/index.js +1 -1
- package/dist/components/help-center-pages/index.cjs +20 -20
- package/dist/components/help-center-pages/index.js +11 -11
- package/dist/components/index.cjs +128 -128
- package/dist/components/index.js +9 -9
- package/dist/components/navigation/index.cjs +2 -2
- package/dist/components/navigation/index.js +1 -1
- package/dist/components/onboarding-guides/index.cjs +5 -5
- package/dist/components/onboarding-guides/index.js +4 -4
- package/dist/components/related-content/index.cjs +3 -3
- package/dist/components/related-content/index.js +2 -2
- package/dist/components/tickets/index.cjs +5 -5
- package/dist/components/tickets/index.js +4 -4
- package/dist/components/ui/index.cjs +2 -2
- package/dist/components/ui/index.js +1 -1
- package/dist/index.cjs +10 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +9 -1
- package/dist/types/video-processing.d.ts +2 -0
- package/dist/types/video-processing.d.ts.map +1 -1
- package/dist/utils/index.cjs +33 -0
- package/dist/utils/index.cjs.map +1 -1
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +30 -1
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/video-bite-id.cjs +37 -0
- package/dist/utils/video-bite-id.cjs.map +1 -0
- package/dist/utils/video-bite-id.d.ts +38 -0
- package/dist/utils/video-bite-id.d.ts.map +1 -0
- package/dist/utils/video-bite-id.js +32 -0
- package/dist/utils/video-bite-id.js.map +1 -0
- package/package.json +7 -1
- package/src/types/video-processing.ts +2 -0
- package/src/utils/index.ts +3 -0
- package/src/utils/video-bite-id.ts +68 -0
- package/dist/chunk-3V5UQ5TU.cjs.map +0 -1
- package/dist/chunk-ZH2OEMQJ.js.map +0 -1
- /package/dist/{chunk-FSOKLOXF.js.map → chunk-6HROGXG6.js.map} +0 -0
- /package/dist/{chunk-2WXQFYNC.js.map → chunk-7NULUAOH.js.map} +0 -0
- /package/dist/{chunk-LYK6D7GU.js.map → chunk-CWQQV4YF.js.map} +0 -0
- /package/dist/{chunk-3OPSNUPJ.js.map → chunk-F24JBANX.js.map} +0 -0
- /package/dist/{chunk-MWIYAVFT.js.map → chunk-GN54QCPI.js.map} +0 -0
- /package/dist/{chunk-32BSKGEI.js.map → chunk-M6RZRURG.js.map} +0 -0
- /package/dist/{chunk-DVYA3BDU.js.map → chunk-O5TONTLW.js.map} +0 -0
- /package/dist/{chunk-E77Z2P3K.js.map → chunk-OY2LB5VP.js.map} +0 -0
- /package/dist/{chunk-GQHXJ37C.js.map → chunk-PMANI6KY.js.map} +0 -0
- /package/dist/{chunk-BMEA5NPX.js.map → chunk-QAIGNMPP.js.map} +0 -0
- /package/dist/{chunk-NFU37EP6.js.map → chunk-U5OADJNL.js.map} +0 -0
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// src/utils/video-bite-id.ts
|
|
4
|
+
var BITE_ID_RE = /^[0-9a-f]{8}$/;
|
|
5
|
+
function generateBiteId() {
|
|
6
|
+
return crypto.randomUUID().replace(/-/g, "").slice(0, 8);
|
|
7
|
+
}
|
|
8
|
+
function ensureBiteIds(bites) {
|
|
9
|
+
const seen = /* @__PURE__ */ new Set();
|
|
10
|
+
let changed = false;
|
|
11
|
+
const out = bites.map((bite) => {
|
|
12
|
+
const valid = typeof bite.id === "string" && BITE_ID_RE.test(bite.id) && !seen.has(bite.id);
|
|
13
|
+
if (valid) {
|
|
14
|
+
seen.add(bite.id);
|
|
15
|
+
return bite;
|
|
16
|
+
}
|
|
17
|
+
changed = true;
|
|
18
|
+
let id = generateBiteId();
|
|
19
|
+
while (seen.has(id)) id = generateBiteId();
|
|
20
|
+
seen.add(id);
|
|
21
|
+
return { ...bite, id };
|
|
22
|
+
});
|
|
23
|
+
return changed ? { bites: out, changed } : { bites, changed };
|
|
24
|
+
}
|
|
25
|
+
function matchBiteElement(existing, incoming) {
|
|
26
|
+
if (existing.id && incoming.id && existing.id === incoming.id) return true;
|
|
27
|
+
if (incoming.url === existing.url) return true;
|
|
28
|
+
if (existing.source_url && incoming.url === existing.source_url) return true;
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
exports.BITE_ID_RE = BITE_ID_RE;
|
|
33
|
+
exports.ensureBiteIds = ensureBiteIds;
|
|
34
|
+
exports.generateBiteId = generateBiteId;
|
|
35
|
+
exports.matchBiteElement = matchBiteElement;
|
|
36
|
+
//# sourceMappingURL=video-bite-id.cjs.map
|
|
37
|
+
//# sourceMappingURL=video-bite-id.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/utils/video-bite-id.ts"],"names":[],"mappings":";;;AAiBO,IAAM,UAAA,GAAa;AAGnB,SAAS,cAAA,GAAyB;AACvC,EAAA,OAAO,MAAA,CAAO,YAAW,CAAE,OAAA,CAAQ,MAAM,EAAE,CAAA,CAAE,KAAA,CAAM,CAAA,EAAG,CAAC,CAAA;AACzD;AAOO,SAAS,cAAyC,KAAA,EAAuD;AAC9G,EAAA,MAAM,IAAA,uBAAW,GAAA,EAAY;AAC7B,EAAA,IAAI,OAAA,GAAU,KAAA;AACd,EAAA,MAAM,GAAA,GAAM,KAAA,CAAM,GAAA,CAAI,CAAA,IAAA,KAAQ;AAC5B,IAAA,MAAM,KAAA,GAAQ,OAAO,IAAA,CAAK,EAAA,KAAO,YAAY,UAAA,CAAW,IAAA,CAAK,IAAA,CAAK,EAAE,CAAA,IAAK,CAAC,IAAA,CAAK,GAAA,CAAI,KAAK,EAAE,CAAA;AAC1F,IAAA,IAAI,KAAA,EAAO;AACT,MAAA,IAAA,CAAK,GAAA,CAAI,KAAK,EAAY,CAAA;AAC1B,MAAA,OAAO,IAAA;AAAA,IACT;AACA,IAAA,OAAA,GAAU,IAAA;AACV,IAAA,IAAI,KAAK,cAAA,EAAe;AACxB,IAAA,OAAO,IAAA,CAAK,GAAA,CAAI,EAAE,CAAA,OAAQ,cAAA,EAAe;AACzC,IAAA,IAAA,CAAK,IAAI,EAAE,CAAA;AACX,IAAA,OAAO,EAAE,GAAG,IAAA,EAAM,EAAA,EAAG;AAAA,EACvB,CAAC,CAAA;AACD,EAAA,OAAO,OAAA,GAAU,EAAE,KAAA,EAAO,GAAA,EAAK,SAAQ,GAAI,EAAE,OAAqB,OAAA,EAAQ;AAC5E;AAiBO,SAAS,gBAAA,CAAiB,UAAwB,QAAA,EAAiC;AACxF,EAAA,IAAI,QAAA,CAAS,MAAM,QAAA,CAAS,EAAA,IAAM,SAAS,EAAA,KAAO,QAAA,CAAS,IAAI,OAAO,IAAA;AACtE,EAAA,IAAI,QAAA,CAAS,GAAA,KAAQ,QAAA,CAAS,GAAA,EAAK,OAAO,IAAA;AAC1C,EAAA,IAAI,SAAS,UAAA,IAAc,QAAA,CAAS,GAAA,KAAQ,QAAA,CAAS,YAAY,OAAO,IAAA;AACxE,EAAA,OAAO,KAAA;AACT","file":"video-bite-id.cjs","sourcesContent":["// Video-bite element identity — SSOT for the stable per-element `id` on\n// `video_bites` / `teasers` jsonb elements (see types/video-processing.ts\n// `VideoTeaser.id`) and for the ONE element-matching predicate shared by the\n// hub's bite writers (`saveEntityVideoBitesResult` merge + the\n// `reconcileIncomingBites` clobber guard).\n//\n// Isomorphic by design: runs in the admin editor (browser) and in the hub's\n// Mux reconciliation sweep (Node). Zero dependencies.\n\n/** Shape every matcher/stamper here operates on — the identity-relevant subset of VideoTeaser. */\nexport interface BiteIdentity {\n id?: string\n url: string\n source_url?: string\n}\n\n/** 8 lowercase hex chars. Collision-safe within one array (n≤~100 → p≈1e-6); identity is always scoped to (table, row, column). */\nexport const BITE_ID_RE = /^[0-9a-f]{8}$/\n\n/** Generate a new 8-hex bite element id. */\nexport function generateBiteId(): string {\n return crypto.randomUUID().replace(/-/g, '').slice(0, 8)\n}\n\n/**\n * Stamp missing ids and re-stamp in-array duplicates (copy/pasted bites).\n * Returns the SAME array reference when nothing changed so callers can cheaply\n * skip persistence (`changed === false` ⇒ no write needed).\n */\nexport function ensureBiteIds<T extends { id?: string }>(bites: readonly T[]): { bites: T[]; changed: boolean } {\n const seen = new Set<string>()\n let changed = false\n const out = bites.map(bite => {\n const valid = typeof bite.id === 'string' && BITE_ID_RE.test(bite.id) && !seen.has(bite.id)\n if (valid) {\n seen.add(bite.id as string)\n return bite\n }\n changed = true\n let id = generateBiteId()\n while (seen.has(id)) id = generateBiteId()\n seen.add(id)\n return { ...bite, id }\n })\n return changed ? { bites: out, changed } : { bites: bites as T[], changed }\n}\n\n/**\n * The ONE element-identity predicate. Matching legs (any-of, NOT\n * short-circuit-on-id-inequality — two sides can carry DIFFERENT ids for\n * the same clip, e.g. a redelivered Vizard ingest regenerates ids):\n * 1. `id` equality — when both sides carry one and they match.\n * 2. `incoming.url === existing.url` — plain url (pre-flip stale sessions,\n * plain re-saves).\n * 3. `incoming.url === existing.source_url` — the incoming side still holds\n * the original storage URL of an element the Mux pipeline already flipped\n * to HLS (redelivered ingests, stale editor arrays).\n *\n * Callers that match a whole incoming array against an existing array MUST\n * consume each existing element at most once (multiset matching) — duplicate\n * incoming urls must not all bind to one element and clone its id.\n */\nexport function matchBiteElement(existing: BiteIdentity, incoming: BiteIdentity): boolean {\n if (existing.id && incoming.id && existing.id === incoming.id) return true\n if (incoming.url === existing.url) return true\n if (existing.source_url && incoming.url === existing.source_url) return true\n return false\n}\n"]}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/** Shape every matcher/stamper here operates on — the identity-relevant subset of VideoTeaser. */
|
|
2
|
+
export interface BiteIdentity {
|
|
3
|
+
id?: string;
|
|
4
|
+
url: string;
|
|
5
|
+
source_url?: string;
|
|
6
|
+
}
|
|
7
|
+
/** 8 lowercase hex chars. Collision-safe within one array (n≤~100 → p≈1e-6); identity is always scoped to (table, row, column). */
|
|
8
|
+
export declare const BITE_ID_RE: RegExp;
|
|
9
|
+
/** Generate a new 8-hex bite element id. */
|
|
10
|
+
export declare function generateBiteId(): string;
|
|
11
|
+
/**
|
|
12
|
+
* Stamp missing ids and re-stamp in-array duplicates (copy/pasted bites).
|
|
13
|
+
* Returns the SAME array reference when nothing changed so callers can cheaply
|
|
14
|
+
* skip persistence (`changed === false` ⇒ no write needed).
|
|
15
|
+
*/
|
|
16
|
+
export declare function ensureBiteIds<T extends {
|
|
17
|
+
id?: string;
|
|
18
|
+
}>(bites: readonly T[]): {
|
|
19
|
+
bites: T[];
|
|
20
|
+
changed: boolean;
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* The ONE element-identity predicate. Matching legs (any-of, NOT
|
|
24
|
+
* short-circuit-on-id-inequality — two sides can carry DIFFERENT ids for
|
|
25
|
+
* the same clip, e.g. a redelivered Vizard ingest regenerates ids):
|
|
26
|
+
* 1. `id` equality — when both sides carry one and they match.
|
|
27
|
+
* 2. `incoming.url === existing.url` — plain url (pre-flip stale sessions,
|
|
28
|
+
* plain re-saves).
|
|
29
|
+
* 3. `incoming.url === existing.source_url` — the incoming side still holds
|
|
30
|
+
* the original storage URL of an element the Mux pipeline already flipped
|
|
31
|
+
* to HLS (redelivered ingests, stale editor arrays).
|
|
32
|
+
*
|
|
33
|
+
* Callers that match a whole incoming array against an existing array MUST
|
|
34
|
+
* consume each existing element at most once (multiset matching) — duplicate
|
|
35
|
+
* incoming urls must not all bind to one element and clone its id.
|
|
36
|
+
*/
|
|
37
|
+
export declare function matchBiteElement(existing: BiteIdentity, incoming: BiteIdentity): boolean;
|
|
38
|
+
//# sourceMappingURL=video-bite-id.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"video-bite-id.d.ts","sourceRoot":"","sources":["../../src/utils/video-bite-id.ts"],"names":[],"mappings":"AASA,kGAAkG;AAClG,MAAM,WAAW,YAAY;IAC3B,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,GAAG,EAAE,MAAM,CAAA;IACX,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB;AAED,mIAAmI;AACnI,eAAO,MAAM,UAAU,QAAkB,CAAA;AAEzC,4CAA4C;AAC5C,wBAAgB,cAAc,IAAI,MAAM,CAEvC;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,CAAC,SAAS;IAAE,EAAE,CAAC,EAAE,MAAM,CAAA;CAAE,EAAE,KAAK,EAAE,SAAS,CAAC,EAAE,GAAG;IAAE,KAAK,EAAE,CAAC,EAAE,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAgB9G;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,YAAY,EAAE,QAAQ,EAAE,YAAY,GAAG,OAAO,CAKxF"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
// src/utils/video-bite-id.ts
|
|
2
|
+
var BITE_ID_RE = /^[0-9a-f]{8}$/;
|
|
3
|
+
function generateBiteId() {
|
|
4
|
+
return crypto.randomUUID().replace(/-/g, "").slice(0, 8);
|
|
5
|
+
}
|
|
6
|
+
function ensureBiteIds(bites) {
|
|
7
|
+
const seen = /* @__PURE__ */ new Set();
|
|
8
|
+
let changed = false;
|
|
9
|
+
const out = bites.map((bite) => {
|
|
10
|
+
const valid = typeof bite.id === "string" && BITE_ID_RE.test(bite.id) && !seen.has(bite.id);
|
|
11
|
+
if (valid) {
|
|
12
|
+
seen.add(bite.id);
|
|
13
|
+
return bite;
|
|
14
|
+
}
|
|
15
|
+
changed = true;
|
|
16
|
+
let id = generateBiteId();
|
|
17
|
+
while (seen.has(id)) id = generateBiteId();
|
|
18
|
+
seen.add(id);
|
|
19
|
+
return { ...bite, id };
|
|
20
|
+
});
|
|
21
|
+
return changed ? { bites: out, changed } : { bites, changed };
|
|
22
|
+
}
|
|
23
|
+
function matchBiteElement(existing, incoming) {
|
|
24
|
+
if (existing.id && incoming.id && existing.id === incoming.id) return true;
|
|
25
|
+
if (incoming.url === existing.url) return true;
|
|
26
|
+
if (existing.source_url && incoming.url === existing.source_url) return true;
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export { BITE_ID_RE, ensureBiteIds, generateBiteId, matchBiteElement };
|
|
31
|
+
//# sourceMappingURL=video-bite-id.js.map
|
|
32
|
+
//# sourceMappingURL=video-bite-id.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/utils/video-bite-id.ts"],"names":[],"mappings":";AAiBO,IAAM,UAAA,GAAa;AAGnB,SAAS,cAAA,GAAyB;AACvC,EAAA,OAAO,MAAA,CAAO,YAAW,CAAE,OAAA,CAAQ,MAAM,EAAE,CAAA,CAAE,KAAA,CAAM,CAAA,EAAG,CAAC,CAAA;AACzD;AAOO,SAAS,cAAyC,KAAA,EAAuD;AAC9G,EAAA,MAAM,IAAA,uBAAW,GAAA,EAAY;AAC7B,EAAA,IAAI,OAAA,GAAU,KAAA;AACd,EAAA,MAAM,GAAA,GAAM,KAAA,CAAM,GAAA,CAAI,CAAA,IAAA,KAAQ;AAC5B,IAAA,MAAM,KAAA,GAAQ,OAAO,IAAA,CAAK,EAAA,KAAO,YAAY,UAAA,CAAW,IAAA,CAAK,IAAA,CAAK,EAAE,CAAA,IAAK,CAAC,IAAA,CAAK,GAAA,CAAI,KAAK,EAAE,CAAA;AAC1F,IAAA,IAAI,KAAA,EAAO;AACT,MAAA,IAAA,CAAK,GAAA,CAAI,KAAK,EAAY,CAAA;AAC1B,MAAA,OAAO,IAAA;AAAA,IACT;AACA,IAAA,OAAA,GAAU,IAAA;AACV,IAAA,IAAI,KAAK,cAAA,EAAe;AACxB,IAAA,OAAO,IAAA,CAAK,GAAA,CAAI,EAAE,CAAA,OAAQ,cAAA,EAAe;AACzC,IAAA,IAAA,CAAK,IAAI,EAAE,CAAA;AACX,IAAA,OAAO,EAAE,GAAG,IAAA,EAAM,EAAA,EAAG;AAAA,EACvB,CAAC,CAAA;AACD,EAAA,OAAO,OAAA,GAAU,EAAE,KAAA,EAAO,GAAA,EAAK,SAAQ,GAAI,EAAE,OAAqB,OAAA,EAAQ;AAC5E;AAiBO,SAAS,gBAAA,CAAiB,UAAwB,QAAA,EAAiC;AACxF,EAAA,IAAI,QAAA,CAAS,MAAM,QAAA,CAAS,EAAA,IAAM,SAAS,EAAA,KAAO,QAAA,CAAS,IAAI,OAAO,IAAA;AACtE,EAAA,IAAI,QAAA,CAAS,GAAA,KAAQ,QAAA,CAAS,GAAA,EAAK,OAAO,IAAA;AAC1C,EAAA,IAAI,SAAS,UAAA,IAAc,QAAA,CAAS,GAAA,KAAQ,QAAA,CAAS,YAAY,OAAO,IAAA;AACxE,EAAA,OAAO,KAAA;AACT","file":"video-bite-id.js","sourcesContent":["// Video-bite element identity — SSOT for the stable per-element `id` on\n// `video_bites` / `teasers` jsonb elements (see types/video-processing.ts\n// `VideoTeaser.id`) and for the ONE element-matching predicate shared by the\n// hub's bite writers (`saveEntityVideoBitesResult` merge + the\n// `reconcileIncomingBites` clobber guard).\n//\n// Isomorphic by design: runs in the admin editor (browser) and in the hub's\n// Mux reconciliation sweep (Node). Zero dependencies.\n\n/** Shape every matcher/stamper here operates on — the identity-relevant subset of VideoTeaser. */\nexport interface BiteIdentity {\n id?: string\n url: string\n source_url?: string\n}\n\n/** 8 lowercase hex chars. Collision-safe within one array (n≤~100 → p≈1e-6); identity is always scoped to (table, row, column). */\nexport const BITE_ID_RE = /^[0-9a-f]{8}$/\n\n/** Generate a new 8-hex bite element id. */\nexport function generateBiteId(): string {\n return crypto.randomUUID().replace(/-/g, '').slice(0, 8)\n}\n\n/**\n * Stamp missing ids and re-stamp in-array duplicates (copy/pasted bites).\n * Returns the SAME array reference when nothing changed so callers can cheaply\n * skip persistence (`changed === false` ⇒ no write needed).\n */\nexport function ensureBiteIds<T extends { id?: string }>(bites: readonly T[]): { bites: T[]; changed: boolean } {\n const seen = new Set<string>()\n let changed = false\n const out = bites.map(bite => {\n const valid = typeof bite.id === 'string' && BITE_ID_RE.test(bite.id) && !seen.has(bite.id)\n if (valid) {\n seen.add(bite.id as string)\n return bite\n }\n changed = true\n let id = generateBiteId()\n while (seen.has(id)) id = generateBiteId()\n seen.add(id)\n return { ...bite, id }\n })\n return changed ? { bites: out, changed } : { bites: bites as T[], changed }\n}\n\n/**\n * The ONE element-identity predicate. Matching legs (any-of, NOT\n * short-circuit-on-id-inequality — two sides can carry DIFFERENT ids for\n * the same clip, e.g. a redelivered Vizard ingest regenerates ids):\n * 1. `id` equality — when both sides carry one and they match.\n * 2. `incoming.url === existing.url` — plain url (pre-flip stale sessions,\n * plain re-saves).\n * 3. `incoming.url === existing.source_url` — the incoming side still holds\n * the original storage URL of an element the Mux pipeline already flipped\n * to HLS (redelivered ingests, stale editor arrays).\n *\n * Callers that match a whole incoming array against an existing array MUST\n * consume each existing element at most once (multiset matching) — duplicate\n * incoming urls must not all bind to one element and clone its id.\n */\nexport function matchBiteElement(existing: BiteIdentity, incoming: BiteIdentity): boolean {\n if (existing.id && incoming.id && existing.id === incoming.id) return true\n if (incoming.url === existing.url) return true\n if (existing.source_url && incoming.url === existing.source_url) return true\n return false\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@flamingo-stack/openframe-frontend-core",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.437",
|
|
4
4
|
"description": "Shared design system and components for all Flamingo platforms",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -198,6 +198,12 @@
|
|
|
198
198
|
"require": "./dist/utils/humanity-signals.cjs",
|
|
199
199
|
"default": "./dist/utils/humanity-signals.js"
|
|
200
200
|
},
|
|
201
|
+
"./utils/video-bite-id": {
|
|
202
|
+
"types": "./dist/utils/video-bite-id.d.ts",
|
|
203
|
+
"import": "./dist/utils/video-bite-id.js",
|
|
204
|
+
"require": "./dist/utils/video-bite-id.cjs",
|
|
205
|
+
"default": "./dist/utils/video-bite-id.js"
|
|
206
|
+
},
|
|
201
207
|
"./styles": {
|
|
202
208
|
"types": "./src/styles/index.d.ts",
|
|
203
209
|
"import": "./src/styles/index.css",
|
|
@@ -12,7 +12,9 @@
|
|
|
12
12
|
* below stays a separate shape by design.
|
|
13
13
|
*/
|
|
14
14
|
export interface VideoTeaser {
|
|
15
|
+
id?: string // Stable 8-char lowercase-hex element id (see utils/video-bite-id.ts). Stamped at creation (editor + AI save) and lazily backfilled by the Mux reconciliation sweep. Identity key for the Mux transcode pipeline — never derived from url (urls have real-world duplicates).
|
|
15
16
|
url: string
|
|
17
|
+
source_url?: string // Original storage URL, set by the Mux pipeline when it flips `url` to the HLS playback URL. Merge-identity leg for redelivered ingests + transcode dedup. Never set by editors.
|
|
16
18
|
title?: string
|
|
17
19
|
thumbnail_url?: string // Optional thumbnail image URL for video preview. If not provided, video player will show first frame automatically.
|
|
18
20
|
published?: boolean // Controls visibility on public preview page (default: false, admin must select)
|
package/src/utils/index.ts
CHANGED
|
@@ -76,6 +76,9 @@ export { getPlatformIconComponent as getPlatformLogo } from './platform-config'
|
|
|
76
76
|
export * from './tool-utils'
|
|
77
77
|
// Shell type utilities
|
|
78
78
|
export * from './shell-utils'
|
|
79
|
+
// Video-bite element identity (id stamping + the ONE matching predicate) —
|
|
80
|
+
// isomorphic; used by the admin bites editor AND the hub's Mux sweep/writers.
|
|
81
|
+
export * from './video-bite-id'
|
|
79
82
|
// OS type utilities
|
|
80
83
|
export * from './os-utils'
|
|
81
84
|
// Phone utilities
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
// Video-bite element identity — SSOT for the stable per-element `id` on
|
|
2
|
+
// `video_bites` / `teasers` jsonb elements (see types/video-processing.ts
|
|
3
|
+
// `VideoTeaser.id`) and for the ONE element-matching predicate shared by the
|
|
4
|
+
// hub's bite writers (`saveEntityVideoBitesResult` merge + the
|
|
5
|
+
// `reconcileIncomingBites` clobber guard).
|
|
6
|
+
//
|
|
7
|
+
// Isomorphic by design: runs in the admin editor (browser) and in the hub's
|
|
8
|
+
// Mux reconciliation sweep (Node). Zero dependencies.
|
|
9
|
+
|
|
10
|
+
/** Shape every matcher/stamper here operates on — the identity-relevant subset of VideoTeaser. */
|
|
11
|
+
export interface BiteIdentity {
|
|
12
|
+
id?: string
|
|
13
|
+
url: string
|
|
14
|
+
source_url?: string
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/** 8 lowercase hex chars. Collision-safe within one array (n≤~100 → p≈1e-6); identity is always scoped to (table, row, column). */
|
|
18
|
+
export const BITE_ID_RE = /^[0-9a-f]{8}$/
|
|
19
|
+
|
|
20
|
+
/** Generate a new 8-hex bite element id. */
|
|
21
|
+
export function generateBiteId(): string {
|
|
22
|
+
return crypto.randomUUID().replace(/-/g, '').slice(0, 8)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Stamp missing ids and re-stamp in-array duplicates (copy/pasted bites).
|
|
27
|
+
* Returns the SAME array reference when nothing changed so callers can cheaply
|
|
28
|
+
* skip persistence (`changed === false` ⇒ no write needed).
|
|
29
|
+
*/
|
|
30
|
+
export function ensureBiteIds<T extends { id?: string }>(bites: readonly T[]): { bites: T[]; changed: boolean } {
|
|
31
|
+
const seen = new Set<string>()
|
|
32
|
+
let changed = false
|
|
33
|
+
const out = bites.map(bite => {
|
|
34
|
+
const valid = typeof bite.id === 'string' && BITE_ID_RE.test(bite.id) && !seen.has(bite.id)
|
|
35
|
+
if (valid) {
|
|
36
|
+
seen.add(bite.id as string)
|
|
37
|
+
return bite
|
|
38
|
+
}
|
|
39
|
+
changed = true
|
|
40
|
+
let id = generateBiteId()
|
|
41
|
+
while (seen.has(id)) id = generateBiteId()
|
|
42
|
+
seen.add(id)
|
|
43
|
+
return { ...bite, id }
|
|
44
|
+
})
|
|
45
|
+
return changed ? { bites: out, changed } : { bites: bites as T[], changed }
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* The ONE element-identity predicate. Matching legs (any-of, NOT
|
|
50
|
+
* short-circuit-on-id-inequality — two sides can carry DIFFERENT ids for
|
|
51
|
+
* the same clip, e.g. a redelivered Vizard ingest regenerates ids):
|
|
52
|
+
* 1. `id` equality — when both sides carry one and they match.
|
|
53
|
+
* 2. `incoming.url === existing.url` — plain url (pre-flip stale sessions,
|
|
54
|
+
* plain re-saves).
|
|
55
|
+
* 3. `incoming.url === existing.source_url` — the incoming side still holds
|
|
56
|
+
* the original storage URL of an element the Mux pipeline already flipped
|
|
57
|
+
* to HLS (redelivered ingests, stale editor arrays).
|
|
58
|
+
*
|
|
59
|
+
* Callers that match a whole incoming array against an existing array MUST
|
|
60
|
+
* consume each existing element at most once (multiset matching) — duplicate
|
|
61
|
+
* incoming urls must not all bind to one element and clone its id.
|
|
62
|
+
*/
|
|
63
|
+
export function matchBiteElement(existing: BiteIdentity, incoming: BiteIdentity): boolean {
|
|
64
|
+
if (existing.id && incoming.id && existing.id === incoming.id) return true
|
|
65
|
+
if (incoming.url === existing.url) return true
|
|
66
|
+
if (existing.source_url && incoming.url === existing.source_url) return true
|
|
67
|
+
return false
|
|
68
|
+
}
|