Class: Typstify::Workspace

Inherits:
Object
  • Object
show all
Defined in:
lib/typstify/workspace.rb

Overview

A throwaway directory holding everything one compile is allowed to see.

<tmp>/main.typ      the template
<tmp>/data.json     your data
<tmp>/shared/       config.shared_dir, copied

Typst is invoked with this directory as its root, so #read("../.env") cannot reach your application — not because we filter it, but because the file is not there. That is the whole security model, and it is why the template is copied to the root of the workspace rather than nested: Typst resolves relative imports against the importing file, so main-at-root is what makes #import "shared/branding.typ" work from any view subdirectory.

Every render gets its own workspace, which is also what makes concurrent rendering safe.

Constant Summary collapse

MAIN =
"main.typ"
DATA =
"data.json"

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(dir, config) ⇒ Workspace

Returns a new instance of Workspace.



40
41
42
43
# File 'lib/typstify/workspace.rb', line 40

def initialize(dir, config)
  @dir = dir
  @config = config
end

Instance Attribute Details

#dirObject (readonly)

Returns the value of attribute dir.



27
28
29
# File 'lib/typstify/workspace.rb', line 27

def dir
  @dir
end

Class Method Details

.build(source:, data:, config: Typstify.config) ⇒ Object

Yields a Workspace and removes it afterwards, exception or not.



30
31
32
33
34
35
36
37
38
# File 'lib/typstify/workspace.rb', line 30

def self.build(source:, data:, config: Typstify.config)
  Dir.mktmpdir("typstify-") do |tmp|
    workspace = new(Pathname.new(tmp), config)
    workspace.write_main(source)
    workspace.write_data(data)
    workspace.copy_shared
    yield workspace
  end
end

Instance Method Details

#copy_sharedObject

Copy config.shared_dir in, if it exists. Copies rather than symlinks: a symlink inside the root points outside the root, which would hand back exactly the file-system access the workspace exists to remove. Symlinks found in the source tree are skipped for the same reason.



61
62
63
64
65
66
67
# File 'lib/typstify/workspace.rb', line 61

def copy_shared
  source = @config.template_root.join(@config.shared_dir)
  return unless source.directory?

  destination = dir.join(@config.shared_dir)
  copy_tree(source, destination)
end

#main_pathObject



45
# File 'lib/typstify/workspace.rb', line 45

def main_path = dir.join(MAIN)

#write_data(data) ⇒ Object

Takes the already-serialised JSON string: validation happens in Document, before a workspace exists.



53
54
55
# File 'lib/typstify/workspace.rb', line 53

def write_data(data)
  File.write(dir.join(DATA), data || "{}", encoding: Encoding::UTF_8)
end

#write_main(source) ⇒ Object



47
48
49
# File 'lib/typstify/workspace.rb', line 47

def write_main(source)
  File.write(main_path, source, encoding: Encoding::UTF_8)
end