API scripts

A script with @api on its first line is the public interface of the scripts under it: it returns what it returns, and only the types its export lines name leave it.

What an ApiScript is

@apiline 1, alone
export Purseone per line, at the top level: a type of its types line, or of a class or module it uses
  • It works like a plain module script; the scripts under it are its insides.
  • A script that uses it sees exactly the exported types.
  • The @api line alone makes it one (shot write, shot edit or the window).
  • The scripts under it stay reachable: use root.Bank.Purse still works.

The example

shot new Wallet.ssp
shot write Bank.shot --project Wallet.ssp            the ApiScript, its text on stdin
shot write Bank/Types.shot --project Wallet.ssp      the types it sees, under it
shot write Bank/Purse.shot --project Wallet.ssp      the class Purse, under it
shot write Main.shot --project Wallet.ssp            the program, which uses it

shot tree --project Wallet.ssp:

Wallet.ssp
./  (the project)
  Bank.shot
    Purse.shot
    Types.shot
  Main.shot

Bank.shot:

@api
types script.Types

use script.Purse

export Purse
export Coins
export Source

return { Purse = Purse }

Bank/Types.shot:

doc { A number of coins }
public type Coins = number

doc { Where coins came from }
public type Source = "Prize" | "Sale"

internal type Entry = { Amount : Coins, From : Source }

Bank/Purse.shot:

types script.Parent.Types

doc { One person's coins }
class Purse = {
    public Owner : string
    public Balance : Coins
    internal History : { Entry }

    constructor new(owner : string)
        local self : Purse = init()
        self.Owner = owner
        self.Balance = 0
        self.History = {}
        return self
    end

    public Add(amount : Coins, from : Source)
        self.Balance += amount
        table.insert(self.History, { Amount = amount, From = from })
    end
}
  • types script.Types reads the Types.shot under it; any Types file it can reach works.
  • export Purse exports the used class's type; export Coins and export Source, two of its types line.
  • Entry stays inside.

What a script that uses it sees

Main.shot:

use Bank

local purse : Bank.Purse = Bank.Purse.new("Ada")
local prize : Bank.Coins = 50
purse:Add(prize, "Prize")
print(purse.Owner .. " has " .. purse.Balance .. " coins")

It checks with 0 errors; shot run Wallet.ssp prints Ada has 50 coins.

  • Bank.Purse is the class (the return) and a type; Bank.Coins and Bank.Source are types.
  • Any other type is an error naming the line to add. local last : Bank.Entry? = nil as line 7:
Wallet.ssp/Main.shot:7:14   TypeError: Unknown type 'Bank.Entry' (Bank exports no type Entry: add export Entry to Bank.shot)

The read tools

shot map, shot api and shot outline show an ApiScript's exports and return first. shot map --project Wallet.ssp:

/
  Bank.shot   apiscript    exports Purse, Coins, Source; returns { Purse = Purse }
  Main.shot   program
Bank/
  Purse.shot  class Purse  One person's coins
  Types.shot  types        public Coins; public Source; internal Entry

shot api --project Wallet.ssp opens with

./
  apiscript Bank  exports Purse, Coins, Source; returns { Purse = Purse }

shot outline Bank --project Wallet.ssp:

apiscript  Bank.shot
  exports Purse, Coins, Source; returns { Purse = Purse }

A module of its own

It may declare one module, named as the file, and returns it; a class, func or interface goes in a script under it.

Prizes.shot, beside Bank.shot:

@api
types script.Types

export Prize

doc { Hands out the prizes }
module Prizes = {
    function Name(prize : Prize) : string
        return "the " .. string.lower(prize) .. " prize"
    end
}

Prizes/Types.shot:

doc { A prize's size }
public type Prize = "Small" | "Big"

internal type Odds = number

After use Prizes, this prints the big prize; Prizes.Odds is unknown, as Bank.Entry is:

local big : Prizes.Prize = "Big"
print(Prizes.Name(big))

shot map:

  Prizes.shot  apiscript module Prizes  exports Prize; returns module Prizes   Hands out the prizes

shot api adds the module's members:

  apiscript Prizes  exports Prize; returns module Prizes
  module Prizes
    Name(prize : Prize) : string

As a project's export

An ApiScript that declares its module can be a project's export: other projects see that module and its exported types only (Externals). With export Prizes in Wallet's header, Shop.ssp's Main.shot

use external "Wallet.ssp"

local big : Prizes.Prize = "Big"
print(Prizes.Name(big))

prints the big prize. Shop's header records:

external "Wallet.ssp" as Wallet interface e99c4d5b0152
    .Name(prize : Prize) -> string
    apiscript Prizes exports Prize; returns module Prizes
    module Prizes
    type Prize = "Small" | "Big"

One that returns a table, like Bank, is a script to other projects. With export Bank:

Shop.ssp/Main.shot:1   "Wallet.ssp" exports the script Bank, and a script can't be used from another project: only a module, a class or a func can

What it builds to

A plain Luau module (Compiled):

  • each export line becomes an export type line in its place, generics included: export Pile for Pile<T> writes export type Pile<T> = Types.Pile<T>;
  • every other type of its types line stays local;
  • the return is its own.

Bank.shot:

--!strict
local Types = require(script.Types)
type Entry = Types.Entry
type PurseClass = Types.PurseClass

local Purse = require(script.Purse)

export type Purse = Types.Purse
export type Coins = Types.Coins
export type Source = Types.Source

return { Purse = Purse }

Mistakes and their errors

Wallet.ssp/Bank.shot:8export Sorce names no type this script sees (did you mean Source?)
Wallet.ssp/Bank.shot:9export Coins is here twice (first at line 7)

Without @api (Sales.shot: use Bank, then export Purse):

Wallet.ssp/Sales.shot:3   export Purse works only in an ApiScript: put @api on this file's first line to make it one

In a class, module, func or Types file:

Wallet.ssp/Bank/Purse.shot:3   export Coins works only in an ApiScript (a script whose first line is @api); this file's public types leave it already, so remove the line

A class in an ApiScript (Market.shot):

Wallet.ssp/Market.shot:4   an ApiScript declares no class (a module or nothing): put class Market in a script under it, then use script.Market and export Market

@api anywhere but alone on the first line:

Wallet.ssp/Bonus.shot:2@api goes on the first line of the file
Wallet.ssp/Bonus.shot:1@api stands alone on the first line of the file