@tinkerlandai/longplay-react-native 0.10.15 → 0.10.16-rc.1
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/IndianaReactNative.podspec +1 -1
- package/README.md +6 -6
- package/dist/index.cjs +2 -2
- package/dist/index.d.cts +4513 -14
- package/dist/index.d.ts +4513 -14
- package/dist/index.js +2 -2
- package/native-sdk/android/assets/indiana/index.html +48 -217
- package/native-sdk/android/java/ai/tinkerland/indiana/CustomerResponseSemantics.kt +1 -0
- package/native-sdk/android/java/ai/tinkerland/indiana/SDKVersion.kt +1 -1
- package/native-sdk/android/java/ai/tinkerland/indiana/ScreenUiSemantics.kt +108 -0
- package/native-sdk/android/java/ai/tinkerland/indiana/WireSchema.kt +2 -1
- package/native-sdk/android/resources/funnel-wire-schemas.json +1 -1
- package/native-sdk/android/resources/renderer-data-url.txt +1 -1
- package/native-sdk/ios/CustomerResponseSemantics.swift +1 -0
- package/native-sdk/ios/SDKVersion.swift +1 -1
- package/native-sdk/ios/ScreenUiSemantics.swift +133 -0
- package/native-sdk/ios/WebView/index.html +48 -217
- package/native-sdk/ios/WebView/renderer-data-url.txt +1 -1
- package/native-sdk/ios/WireSchema.swift +2 -2
- package/native-sdk/ios/funnel-wire-schemas.json +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
import CoreFoundation
|
|
3
|
+
|
|
4
|
+
/// Display-only rules; never evaluates expressions or grants commercial authority.
|
|
5
|
+
enum IndianaScreenUiSemantics {
|
|
6
|
+
typealias O = [String: Any]
|
|
7
|
+
static let reserved: Set<String> = ["constructor", "prototype", "__proto__", "__defineGetter__", "__defineSetter__", "__lookupGetter__", "__lookupSetter__", "hasOwnProperty", "isPrototypeOf", "propertyIsEnumerable", "toString", "toLocaleString", "valueOf"]
|
|
8
|
+
static func require(_ valid: Bool) throws { if !valid { throw IndianaClientError.invalidResponse } }
|
|
9
|
+
static func identifier(_ value: Any?) -> Bool { guard let s = value as? String else { return false }; return s.range(of: "^[A-Za-z][A-Za-z0-9_-]{0,63}$", options: .regularExpression) != nil && !reserved.contains(s) }
|
|
10
|
+
static func object(_ value: Any?, _ allowed: Set<String>? = nil) throws -> O {
|
|
11
|
+
guard let result = value as? O else { throw IndianaClientError.invalidResponse }
|
|
12
|
+
try require(Set(result.keys).isDisjoint(with: reserved) && (allowed == nil || Set(result.keys).isSubset(of: allowed!)))
|
|
13
|
+
return result
|
|
14
|
+
}
|
|
15
|
+
static func number(_ value: Any?) -> Double? { guard let n = value as? NSNumber, CFGetTypeID(n) != CFBooleanGetTypeID(), n.doubleValue.isFinite else { return nil }; return n.doubleValue }
|
|
16
|
+
static func bool(_ value: Any?) -> Bool { guard let n = value as? NSNumber else { return false }; return CFGetTypeID(n) == CFBooleanGetTypeID() }
|
|
17
|
+
static func scalar(_ value: Any?) -> Bool { if value is NSNull { return true }; if let s = value as? String { return s.unicodeScalars.count <= 2000 }; return bool(value) || number(value) != nil }
|
|
18
|
+
static func same(_ a: Any, _ b: Any) -> Bool { if let x = number(a), let y = number(b) { return x == y }; if a is NSNull { return b is NSNull }; if bool(a) { return bool(b) && (a as! NSNumber).boolValue == (b as! NSNumber).boolValue }; if let x = a as? String, let y = b as? String { return x == y }; return false }
|
|
19
|
+
static func stateValue(_ d: O, _ value: Any?) -> Bool {
|
|
20
|
+
switch d["type"] as? String {
|
|
21
|
+
case "enum": guard let rows = d["values"] as? [Any], let value else { return false }; return rows.contains { same($0, value) }
|
|
22
|
+
case "boolean": return bool(value)
|
|
23
|
+
case "text": return value is NSNull || (value as? String).map { Double($0.utf16.count) <= (number(d["maxLength"]) ?? -1) } == true
|
|
24
|
+
case "number": guard let n = number(value), let lo = number(d["min"]), let hi = number(d["max"]), let step = number(d["step"]), n >= lo, n <= hi, step > 0 else { return false }; let steps = (n-lo)/step; return steps.isFinite && abs(steps-steps.rounded()) <= min(1e-7, max(1,abs(steps))*Double.ulpOfOne*8)
|
|
25
|
+
default: return false
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
static func state(_ value: Any?) throws {
|
|
29
|
+
let d = try object(value), type = d["type"] as? String ?? ""
|
|
30
|
+
let fields: [String: Set<String>] = ["enum":["type","values","default"],"number":["type","min","max","step","default"],"text":["type","maxLength","default"],"boolean":["type","default"]]
|
|
31
|
+
try require(fields[type] == Set(d.keys) && scalar(d["default"]))
|
|
32
|
+
if type == "enum" { guard let rows = d["values"] as? [Any] else { throw IndianaClientError.invalidResponse }; try require((1...64).contains(rows.count) && rows.allSatisfy(scalar)); for i in rows.indices { try require(!rows.prefix(i).contains { same($0, rows[i]) }) } }
|
|
33
|
+
if type == "number" { guard let lo=number(d["min"]), let hi=number(d["max"]), let step=number(d["step"]) else { throw IndianaClientError.invalidResponse }; try require(lo <= hi && step > 0) }
|
|
34
|
+
if type == "text" { guard let length=number(d["maxLength"]) else { throw IndianaClientError.invalidResponse }; try require(length >= 0 && length <= 2000 && length.rounded() == length) }
|
|
35
|
+
try require(stateValue(d,d["default"]))
|
|
36
|
+
}
|
|
37
|
+
static func arity(_ value: Any?) throws {
|
|
38
|
+
let d = try object(value,["op","args"])
|
|
39
|
+
guard Set(d.keys) == ["op","args"], let op=d["op"] as? String, let args=d["args"] as? [Any] else { throw IndianaClientError.invalidResponse }
|
|
40
|
+
let count=args.count; let valid: Bool
|
|
41
|
+
switch op {
|
|
42
|
+
case "now": valid=count==0
|
|
43
|
+
case "concat": valid=true
|
|
44
|
+
case "lookup": valid=count>=4 && count%2==0
|
|
45
|
+
case "if": valid=count==3
|
|
46
|
+
case "sub","div","eq","ne","gt","gte","lt","lte": valid=count==2
|
|
47
|
+
case "round","floor","not","number","parse_number","money","duration": valid=count==1
|
|
48
|
+
case "add","mul","min","max","and","or": valid=count>=1
|
|
49
|
+
default: valid=false
|
|
50
|
+
}; try require(valid)
|
|
51
|
+
}
|
|
52
|
+
static func expression(_ value: Any, _ budget: inout Int) throws -> Set<String> {
|
|
53
|
+
var pending: [(Any,Int)] = [(value,1)], refs = Set<String>()
|
|
54
|
+
while let (item,depth)=pending.popLast() {
|
|
55
|
+
budget += 1; try require(budget<=2000 && depth<=12)
|
|
56
|
+
if scalar(item) { continue }
|
|
57
|
+
let d=try object(item)
|
|
58
|
+
if let ref=d["ref"] { try require(Set(d.keys)==["ref"] && identifier(ref)); refs.insert(ref as! String) }
|
|
59
|
+
else { try arity(d); let args=d["args"] as! [Any]; try require(args.count<=2000); pending.append(contentsOf:args.map { ($0,depth+1) }) }
|
|
60
|
+
}; return refs
|
|
61
|
+
}
|
|
62
|
+
static func binding(_ value: Any?) throws {
|
|
63
|
+
let d=try object(value,["target","property","expression","className"]), property=d["property"] as? String ?? ""
|
|
64
|
+
try require(identifier(d["target"]) && d["expression"] != nil && ["text","visible","disabled","checked","value","class","aria-label","aria-selected","aria-checked","aria-valuetext","tabindex"].contains(property))
|
|
65
|
+
try require((property=="class")==((d["className"]) != nil) && (d["className"] == nil || identifier(d["className"])))
|
|
66
|
+
}
|
|
67
|
+
static func ui(_ value: Any?) throws {
|
|
68
|
+
let ui=try object(value,["state","derived","bindings","controls","timers","effects","clock"])
|
|
69
|
+
let state=try object(ui["state"] ?? O()), derived=try object(ui["derived"] ?? O())
|
|
70
|
+
try require(state.count<=32 && derived.count<=96 && Set(state.keys).isDisjoint(with:derived.keys) && Array(state.keys).allSatisfy(identifier) && Array(derived.keys).allSatisfy(identifier))
|
|
71
|
+
for definition in state.values { try self.state(definition) }
|
|
72
|
+
let available=Set(state.keys).union(derived.keys); var budget=0, dependencies: [String:Set<String>]=[:]
|
|
73
|
+
for (id,expr) in derived { let refs=try expression(expr,&budget); try require(refs.isSubset(of:available)); dependencies[id]=refs }
|
|
74
|
+
var active=Set<String>(), visited=Set<String>()
|
|
75
|
+
func visit(_ id: String) throws { try require(!active.contains(id)); if visited.contains(id) { return }; active.insert(id); for ref in dependencies[id] ?? [] where derived[ref] != nil { try visit(ref) }; active.remove(id); visited.insert(id) }
|
|
76
|
+
for id in derived.keys { try visit(id) }
|
|
77
|
+
func read(_ value: Any?) throws { guard let value else { throw IndianaClientError.invalidResponse }; try require(try expression(value,&budget).isSubset(of:available)) }
|
|
78
|
+
func updates(_ value: Any?) throws { let set=try object(value); try require((1...32).contains(set.count) && Set(set.keys).isSubset(of:state.keys)); for expr in set.values { try read(expr) } }
|
|
79
|
+
func rows(_ key: String, _ max: Int) throws -> [Any] { if ui[key] == nil { return [] }; guard let rows=ui[key] as? [Any] else { throw IndianaClientError.invalidResponse }; try require(rows.count<=max); return rows }
|
|
80
|
+
try require(ui["clock"] == nil || bool(ui["clock"]))
|
|
81
|
+
for raw in try rows("bindings",256) { try binding(raw); try read(try object(raw)["expression"]) }
|
|
82
|
+
var effectTargets = Set<String>()
|
|
83
|
+
let effectBounds: [String:(Double,Double)] = ["speed":(0,4),"pitch":(0.05,4),"fill":(0.05,1),"feather":(0,1),"warp":(0,4),"warpScale":(0,32),"detailScale":(0,32),"waveScale":(0,32),"falloff":(0.1,32),"brightness":(0,32),"ambient":(0,1),"gamma":(0.05,8),"vignette":(0,1),"opacity":(0,1)]
|
|
84
|
+
for raw in try rows("effects",8) {
|
|
85
|
+
let e=try object(raw)
|
|
86
|
+
try require(identifier(e["target"]))
|
|
87
|
+
let target=e["target"] as! String; try require(effectTargets.insert(target).inserted)
|
|
88
|
+
if e["type"] as? String=="number_counter" {
|
|
89
|
+
_=try object(e,["type","target","value","prefix","format"])
|
|
90
|
+
try read(e["value"])
|
|
91
|
+
if let n=number(e["value"]) { try require(abs(n)<=1e12) }
|
|
92
|
+
if e["prefix"] != nil { guard let prefix=e["prefix"] as? String else { throw IndianaClientError.invalidResponse }; try require(prefix.unicodeScalars.count<=20) }
|
|
93
|
+
if e["format"] != nil { try require(["number","short"].contains(e["format"] as? String ?? "")) }
|
|
94
|
+
continue
|
|
95
|
+
}
|
|
96
|
+
_=try object(e,Set(effectBounds.keys).union(["type","target","color","hotColor","enabled"]))
|
|
97
|
+
try require(e["type"] as? String=="mosaic_waves")
|
|
98
|
+
for key in ["color","hotColor"] { guard let color=e[key] as? String else { throw IndianaClientError.invalidResponse }; try require(color.utf8.count==7 && color.range(of:"^#[0-9a-fA-F]{6}$",options:.regularExpression) != nil) }
|
|
99
|
+
for (key,(low,high)) in effectBounds where e[key] != nil { guard let n=number(e[key]) else { throw IndianaClientError.invalidResponse }; try require(n>=low && n<=high) }
|
|
100
|
+
if e["enabled"] != nil { try read(e["enabled"]) }
|
|
101
|
+
}
|
|
102
|
+
let controls = try rows("controls",64), controlTargets = Set((try rows("controls",64)).compactMap { ($0 as? O)?["target"] as? String })
|
|
103
|
+
var navigationTargets = Set<String>()
|
|
104
|
+
var inactiveTargets = Set<String>()
|
|
105
|
+
for raw in controls {
|
|
106
|
+
let c=try object(raw), kind=c["type"] as? String ?? ""
|
|
107
|
+
let fields: [String:Set<String>]=["input":["type","target","state","set"],"action":["type","target","set"],"carousel":["type","target","state","initialIndex","maxWidth","previousTarget","nextTarget","inactiveTargets","inactiveMaxWidth"],"scroll_latch":["type","target","state"]]
|
|
108
|
+
try require(fields[kind] != nil && Set(c.keys).isSubset(of:fields[kind]!) && identifier(c["target"]))
|
|
109
|
+
if kind != "action" {
|
|
110
|
+
guard let id=c["state"] as? String, let rawState=state[id] else { throw IndianaClientError.invalidResponse }; let d=try object(rawState)
|
|
111
|
+
if kind=="scroll_latch" { try require(d["type"] as? String=="boolean") }
|
|
112
|
+
if kind=="input", d["type"] as? String=="enum" { try require((d["values"] as! [Any]).allSatisfy { $0 is String }) }
|
|
113
|
+
if kind=="carousel", c["inactiveMaxWidth"] != nil { guard let width=number(c["inactiveMaxWidth"]) else { throw IndianaClientError.invalidResponse }; try require(c["inactiveTargets"] != nil && width>=1 && width<=10000 && width.rounded()==width) }
|
|
114
|
+
if kind=="carousel", c["inactiveTargets"] != nil { guard let ids=c["inactiveTargets"] as? [Any] else { throw IndianaClientError.invalidResponse }; try require(ids.count<=20); for id in ids { guard let article=id as? String else { throw IndianaClientError.invalidResponse }; try require(identifier(article) && article != c["target"] as? String && inactiveTargets.insert(article).inserted) } }
|
|
115
|
+
if kind=="carousel" { for field in ["previousTarget","nextTarget"] where c[field] != nil { guard let nav=c[field] as? String else { throw IndianaClientError.invalidResponse }; try require(identifier(nav) && !controlTargets.contains(nav) && navigationTargets.insert(nav).inserted) } }
|
|
116
|
+
if kind=="carousel" { let lo=number(d["min"]) ?? -1, hi=number(d["max"]) ?? -1; try require(d["type"] as? String=="number" && lo>=0 && lo.rounded()==lo && hi.rounded()==hi && number(d["step"])==1 && (c["initialIndex"]==nil || stateValue(d,c["initialIndex"]))); if c["maxWidth"] != nil { guard let width=number(c["maxWidth"]) else { throw IndianaClientError.invalidResponse }; try require(width>=1 && width<=10000 && width.rounded()==width) } }
|
|
117
|
+
}
|
|
118
|
+
if kind=="action" || c["set"] != nil { try updates(c["set"]) }
|
|
119
|
+
}
|
|
120
|
+
for raw in try rows("timers",4) { let timer=try object(raw,["afterMs","set"]); guard let delay=number(timer["afterMs"]) else { throw IndianaClientError.invalidResponse }; try require(delay>=0 && delay<=60000 && delay.rounded()==delay); try updates(timer["set"]) }
|
|
121
|
+
}
|
|
122
|
+
static func validate(_ rule: String, _ value: Any?) throws {
|
|
123
|
+
switch rule {
|
|
124
|
+
case "screenUi": try ui(value)
|
|
125
|
+
case "screenUiState": try state(value)
|
|
126
|
+
case "screenUiIdentifier": try require(identifier(value))
|
|
127
|
+
case "screenUiArity": try arity(value)
|
|
128
|
+
case "screenUiBinding": try binding(value)
|
|
129
|
+
case "screenDesktopWidth": let d=try object(value,["min","max","viewport"]); guard let lo=number(d["min"]), let hi=number(d["max"]), let width=number(d["viewport"]) else { throw IndianaClientError.invalidResponse }; try require(d.count==3 && lo>=1 && lo<=hi && hi<=1800 && width>=1 && width<=100)
|
|
130
|
+
default: throw IndianaClientError.invalidResponse
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|