@sprig-and-prose/scene-example 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/package.json +17 -0
- package/scripts/compile.js +30 -0
- package/scripts/run-skills-many.js +44 -0
- package/scripts/run-skills.js +49 -0
- package/scripts/run-tools.js +91 -0
- package/scripts/run-trace.js +22 -0
- package/sprig/actor.scene.prose +8 -0
- package/sprig/skills-many.scene.prose +52 -0
- package/sprig/skills.scene.prose +85 -0
- package/sprig/tools.scene.prose +82 -0
package/package.json
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@sprig-and-prose/scene-example",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "Example package: compile sprig prose to scene manifests",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"compile": "node scripts/compile.js",
|
|
8
|
+
"run:tools": "npx tsx scripts/run-tools.js",
|
|
9
|
+
"run:skills": "npx tsx scripts/run-skills.js",
|
|
10
|
+
"run:skills-many": "npx tsx scripts/run-skills-many.js",
|
|
11
|
+
"run:trace": "npx tsx scripts/run-trace.js"
|
|
12
|
+
},
|
|
13
|
+
"dependencies": {
|
|
14
|
+
"@sprig-and-prose/sprig-scene-engine": "*",
|
|
15
|
+
"@sprig-and-prose/sprig-scenes": "*"
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Compile sprig/*.prose into .sprig/*.scene.json
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { readdir, writeFile, mkdir } from 'node:fs/promises';
|
|
6
|
+
import { join, dirname } from 'node:path';
|
|
7
|
+
import { fileURLToPath } from 'node:url';
|
|
8
|
+
import { compileSceneFile } from '@sprig-and-prose/sprig-scenes/compiler';
|
|
9
|
+
|
|
10
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
11
|
+
const __dirname = dirname(__filename);
|
|
12
|
+
|
|
13
|
+
const sprigDir = join(__dirname, '..', 'sprig');
|
|
14
|
+
const outDir = join(__dirname, '..', '.sprig');
|
|
15
|
+
|
|
16
|
+
const entries = await readdir(sprigDir);
|
|
17
|
+
const proseFiles = entries.filter((entry) => entry.endsWith('.prose')).sort();
|
|
18
|
+
|
|
19
|
+
await mkdir(outDir, { recursive: true });
|
|
20
|
+
|
|
21
|
+
for (const entry of proseFiles) {
|
|
22
|
+
const inputPath = join(sprigDir, entry);
|
|
23
|
+
const outputBase = entry.endsWith('.scene.prose')
|
|
24
|
+
? entry.replace(/\.scene\.prose$/u, '.scene.json')
|
|
25
|
+
: `${entry.replace(/\.prose$/u, '')}.scene.json`;
|
|
26
|
+
const outputPath = join(outDir, outputBase);
|
|
27
|
+
const manifest = await compileSceneFile(inputPath);
|
|
28
|
+
const json = `${JSON.stringify(manifest, null, 2)}\n`;
|
|
29
|
+
await writeFile(outputPath, json, 'utf-8');
|
|
30
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Harness: load skills-many.scene.json, set test data, evaluate PlayerSkillMatches (many resolver).
|
|
3
|
+
* Run after compile: npm run compile && npm run run:skills-many
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { readFile } from 'node:fs/promises';
|
|
7
|
+
import { join, dirname } from 'node:path';
|
|
8
|
+
import { fileURLToPath } from 'node:url';
|
|
9
|
+
import { createSceneRuntime } from '@sprig-and-prose/sprig-scene-engine';
|
|
10
|
+
|
|
11
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
12
|
+
const __dirname = dirname(__filename);
|
|
13
|
+
|
|
14
|
+
const MANIFEST_PATH = join(__dirname, '..', '.sprig', 'skills-many.scene.json');
|
|
15
|
+
|
|
16
|
+
const skills = [
|
|
17
|
+
{ id: 10, name: 'X' },
|
|
18
|
+
{ id: 10, name: 'X2' },
|
|
19
|
+
{ id: 20, name: 'Y' },
|
|
20
|
+
];
|
|
21
|
+
|
|
22
|
+
const rawPlayerSkills = [
|
|
23
|
+
{ playerId: 1, skillId: 10, experience: 5 },
|
|
24
|
+
{ playerId: 2, skillId: 20, experience: 7 },
|
|
25
|
+
];
|
|
26
|
+
|
|
27
|
+
async function main() {
|
|
28
|
+
const raw = await readFile(MANIFEST_PATH, 'utf-8');
|
|
29
|
+
const manifest = JSON.parse(raw);
|
|
30
|
+
|
|
31
|
+
const runtime = createSceneRuntime(manifest);
|
|
32
|
+
runtime.setPortalData('Skills', skills);
|
|
33
|
+
runtime.setPortalData('RawPlayerSkills', rawPlayerSkills);
|
|
34
|
+
|
|
35
|
+
console.log('Derived results for skills-many.scene.json (many resolver demo):\n');
|
|
36
|
+
|
|
37
|
+
const result = runtime.getDerived('PlayerSkillMatches');
|
|
38
|
+
console.log('PlayerSkillMatches:', JSON.stringify(result, null, 2));
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
main().catch((err) => {
|
|
42
|
+
console.error(err);
|
|
43
|
+
process.exit(1);
|
|
44
|
+
});
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Harness: load skills.scene.json, set test data, evaluate PlayerSkills derived block.
|
|
3
|
+
* Run after compile: npm run compile && npm run run:skills
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { readFile } from 'node:fs/promises';
|
|
7
|
+
import { join, dirname } from 'node:path';
|
|
8
|
+
import { fileURLToPath } from 'node:url';
|
|
9
|
+
import { createSceneRuntime } from '@sprig-and-prose/sprig-scene-engine';
|
|
10
|
+
|
|
11
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
12
|
+
const __dirname = dirname(__filename);
|
|
13
|
+
|
|
14
|
+
const MANIFEST_PATH = join(__dirname, '..', '.sprig', 'skills.scene.json');
|
|
15
|
+
|
|
16
|
+
const players = [
|
|
17
|
+
{ id: 1, name: 'Alice' },
|
|
18
|
+
{ id: 2, name: 'Bob' },
|
|
19
|
+
];
|
|
20
|
+
|
|
21
|
+
const skills = [
|
|
22
|
+
{ id: 10, name: 'Coding' },
|
|
23
|
+
{ id: 20, name: 'Hacking' },
|
|
24
|
+
];
|
|
25
|
+
|
|
26
|
+
const rawPlayerSkills = [
|
|
27
|
+
{ playerId: 1, skillId: 10, experience: 5 },
|
|
28
|
+
{ playerId: 2, skillId: 20, experience: 7 },
|
|
29
|
+
];
|
|
30
|
+
|
|
31
|
+
async function main() {
|
|
32
|
+
const raw = await readFile(MANIFEST_PATH, 'utf-8');
|
|
33
|
+
const manifest = JSON.parse(raw);
|
|
34
|
+
|
|
35
|
+
const runtime = createSceneRuntime(manifest);
|
|
36
|
+
runtime.setPortalData('Players', players);
|
|
37
|
+
runtime.setPortalData('Skills', skills);
|
|
38
|
+
runtime.setPortalData('RawPlayerSkills', rawPlayerSkills);
|
|
39
|
+
|
|
40
|
+
console.log('Derived results for skills.scene.json with test data:\n');
|
|
41
|
+
|
|
42
|
+
const result = runtime.getDerived('PlayerSkills');
|
|
43
|
+
console.log('PlayerSkills:', JSON.stringify(result, null, 2));
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
main().catch((err) => {
|
|
47
|
+
console.error(err);
|
|
48
|
+
process.exit(1);
|
|
49
|
+
});
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Harness: load tools.scene.json, set test data, evaluate derived blocks and print results.
|
|
3
|
+
* Run after compile: npm run compile && npm run run:tools
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { readFile } from 'node:fs/promises';
|
|
7
|
+
import { join, dirname } from 'node:path';
|
|
8
|
+
import { fileURLToPath } from 'node:url';
|
|
9
|
+
import { createSceneRuntime } from '@sprig-and-prose/sprig-scene-engine';
|
|
10
|
+
|
|
11
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
12
|
+
const __dirname = dirname(__filename);
|
|
13
|
+
|
|
14
|
+
const MANIFEST_PATH = join(__dirname, '..', '.sprig', 'tools.scene.json');
|
|
15
|
+
|
|
16
|
+
// Test data matching portals: ContainerRows (array of ContainerRow), AnotherToolPortal (array of Paperdoll)
|
|
17
|
+
const containerRows = [
|
|
18
|
+
{
|
|
19
|
+
item: {
|
|
20
|
+
id: 10,
|
|
21
|
+
tools: [
|
|
22
|
+
{ id: 1, name: 'Hammer' },
|
|
23
|
+
{ id: 2, name: 'Wrench' },
|
|
24
|
+
],
|
|
25
|
+
},
|
|
26
|
+
count: 2,
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
item: {
|
|
30
|
+
id: 11,
|
|
31
|
+
tools: [
|
|
32
|
+
{ id: 3, name: 'Hammer' },
|
|
33
|
+
{ id: 4, name: 'Pliers' },
|
|
34
|
+
],
|
|
35
|
+
},
|
|
36
|
+
count: 1,
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
item: {
|
|
40
|
+
id: 12,
|
|
41
|
+
tools: [{ id: 5, name: 'Wrench' }],
|
|
42
|
+
},
|
|
43
|
+
count: 1,
|
|
44
|
+
},
|
|
45
|
+
];
|
|
46
|
+
|
|
47
|
+
const anotherToolPortal = [
|
|
48
|
+
{
|
|
49
|
+
tools: [{ id: 10, name: 'Brush' }],
|
|
50
|
+
},
|
|
51
|
+
];
|
|
52
|
+
|
|
53
|
+
const DERIVED_BLOCK_NAMES = [
|
|
54
|
+
'RowCount',
|
|
55
|
+
'ToolNames',
|
|
56
|
+
'ToolCount',
|
|
57
|
+
'ToolString1',
|
|
58
|
+
'ToolString2',
|
|
59
|
+
'MultipleExtracts',
|
|
60
|
+
];
|
|
61
|
+
|
|
62
|
+
async function main() {
|
|
63
|
+
const raw = await readFile(MANIFEST_PATH, 'utf-8');
|
|
64
|
+
const manifest = JSON.parse(raw);
|
|
65
|
+
|
|
66
|
+
const runtime = createSceneRuntime(manifest);
|
|
67
|
+
runtime.setPortalData('ContainerRows', containerRows);
|
|
68
|
+
runtime.setPortalData('AnotherToolPortal', anotherToolPortal);
|
|
69
|
+
|
|
70
|
+
console.log('Derived results for tools.scene.json with test data:\n');
|
|
71
|
+
|
|
72
|
+
for (const name of DERIVED_BLOCK_NAMES) {
|
|
73
|
+
try {
|
|
74
|
+
const result = runtime.getDerived(name);
|
|
75
|
+
const display =
|
|
76
|
+
typeof result === 'string'
|
|
77
|
+
? result
|
|
78
|
+
: Array.isArray(result)
|
|
79
|
+
? JSON.stringify(result)
|
|
80
|
+
: JSON.stringify(result);
|
|
81
|
+
console.log(`${name}: ${display}`);
|
|
82
|
+
} catch (err) {
|
|
83
|
+
console.log(`${name}: ERROR ${err.message}`);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
main().catch((err) => {
|
|
89
|
+
console.error(err);
|
|
90
|
+
process.exit(1);
|
|
91
|
+
});
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { evaluate, renderTraceToConsole, renderNarrative } from '@sprig-and-prose/sprig-scene-engine';
|
|
2
|
+
import { readFile } from 'node:fs/promises';
|
|
3
|
+
|
|
4
|
+
const manifest = JSON.parse(await readFile('.sprig/skills.scene.json', 'utf-8'));
|
|
5
|
+
const portals = {
|
|
6
|
+
Players: [{ id: 1, name: 'Alice' }, { id: 2, name: 'Bob' }],
|
|
7
|
+
Skills: [{ id: 10, name: 'Coding' }, { id: 20, name: 'Hacking' }],
|
|
8
|
+
RawPlayerSkills: [
|
|
9
|
+
{ playerId: 1, skillId: 10, experience: 5 },
|
|
10
|
+
{ playerId: 2, skillId: 20, experience: 7 },
|
|
11
|
+
],
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
const { results, trace } = evaluate(manifest, portals, { trace: true });
|
|
15
|
+
|
|
16
|
+
console.log('Results:', JSON.stringify(results, null, 2));
|
|
17
|
+
console.log('\n--- Trace (structured) ---');
|
|
18
|
+
console.log(JSON.stringify(trace, null, 2));
|
|
19
|
+
console.log('\n--- Trace (readable) ---');
|
|
20
|
+
console.log(renderTraceToConsole(trace));
|
|
21
|
+
console.log('\n--- Trace (narrative) ---');
|
|
22
|
+
console.log(renderNarrative(trace));
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
scene SkillManyScene {
|
|
2
|
+
actors {
|
|
3
|
+
Skill {
|
|
4
|
+
kind {
|
|
5
|
+
id { integer }
|
|
6
|
+
name { string }
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
identity { id }
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
RawPlayerSkill {
|
|
13
|
+
kind {
|
|
14
|
+
playerId { integer }
|
|
15
|
+
skillId { integer }
|
|
16
|
+
experience { integer }
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
identity { playerId skillId }
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
portals {
|
|
24
|
+
Skills {
|
|
25
|
+
kind { [ Skill ] }
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
RawPlayerSkills {
|
|
29
|
+
kind { [ RawPlayerSkill ] }
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
derived {
|
|
34
|
+
PlayerSkillMatches {
|
|
35
|
+
each {
|
|
36
|
+
from { RawPlayerSkills[] }
|
|
37
|
+
named { row }
|
|
38
|
+
as {
|
|
39
|
+
skillMatches {
|
|
40
|
+
many {
|
|
41
|
+
from { Skills[] }
|
|
42
|
+
matches { row.skillId }
|
|
43
|
+
by { id }
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
experience { row.experience }
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
scene SkillScene {
|
|
2
|
+
actors {
|
|
3
|
+
Skill {
|
|
4
|
+
kind {
|
|
5
|
+
id { integer }
|
|
6
|
+
name { string }
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
identity { id }
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
Player {
|
|
13
|
+
kind {
|
|
14
|
+
id { integer }
|
|
15
|
+
name { string }
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
identity { id }
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
RawPlayerSkill {
|
|
22
|
+
kind {
|
|
23
|
+
playerId { integer }
|
|
24
|
+
skillId { integer }
|
|
25
|
+
experience { integer }
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
identity { playerId, skillId }
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
PlayerSkill {
|
|
32
|
+
kind {
|
|
33
|
+
player { Player }
|
|
34
|
+
skill { Skill }
|
|
35
|
+
experience { integer }
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
identity { player skill }
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
portals {
|
|
43
|
+
Players {
|
|
44
|
+
kind { [ Player ] }
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
Skills {
|
|
48
|
+
kind { [ Skill ] }
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
RawPlayerSkills {
|
|
52
|
+
kind { [ RawPlayerSkill ] }
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
derived {
|
|
57
|
+
PlayerSkills {
|
|
58
|
+
kind { [ PlayerSkill ] }
|
|
59
|
+
|
|
60
|
+
each {
|
|
61
|
+
from { RawPlayerSkills[] }
|
|
62
|
+
named { row }
|
|
63
|
+
as {
|
|
64
|
+
player {
|
|
65
|
+
one {
|
|
66
|
+
from { Players[] }
|
|
67
|
+
matches { row.playerId }
|
|
68
|
+
by { id }
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
skill {
|
|
73
|
+
one {
|
|
74
|
+
from { Skills[] }
|
|
75
|
+
matches { row.skillId }
|
|
76
|
+
by { id }
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
experience { row.experience }
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
scene ToolScene {
|
|
2
|
+
actors {
|
|
3
|
+
Tool {
|
|
4
|
+
kind {
|
|
5
|
+
id { integer }
|
|
6
|
+
name { string }
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
identity { id }
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
Item {
|
|
13
|
+
kind {
|
|
14
|
+
id { integer }
|
|
15
|
+
tools { [ Tool ] }
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
identity { id }
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
ContainerRow {
|
|
22
|
+
kind {
|
|
23
|
+
item { Item }
|
|
24
|
+
count { integer }
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
identity { item }
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
Paperdoll {
|
|
31
|
+
kind {
|
|
32
|
+
tools { [ Tool ] }
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
portals {
|
|
38
|
+
ContainerRows {
|
|
39
|
+
kind { [ ContainerRow ] }
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
AnotherToolPortal {
|
|
43
|
+
kind { [ Paperdoll ] }
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
derived {
|
|
48
|
+
RowCount {
|
|
49
|
+
count { ContainerRows }
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
MultipleExtracts {
|
|
53
|
+
values ContainerTools { ContainerRows[].item.tools[] }
|
|
54
|
+
values OtherTools { AnotherToolPortal[].tools[] }
|
|
55
|
+
values CombinedTools { ContainerTools[] OtherTools[] }
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
ToolNames {
|
|
59
|
+
values { ContainerRows[].item.tools[].name }
|
|
60
|
+
unique { values }
|
|
61
|
+
sort {
|
|
62
|
+
from { unique }
|
|
63
|
+
order { ascending }
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
ToolCount {
|
|
68
|
+
count { ToolNames }
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
ToolString1 {
|
|
72
|
+
join { ToolNames with { ', ' } }
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
ToolString2 {
|
|
76
|
+
join {
|
|
77
|
+
from { ToolNames }
|
|
78
|
+
with { ', ' }
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|